<template>
  <view class="container">
    <!-- 顶部区域 -->
    <view class="header">
      <!-- 顶部logo和导航 -->
      <view class="top-bar">
        <view class="logo-section">
          <image
            class="fixed-logo"
            :src="getImageUrl('资源 3-8.png')"
            mode="widthFix"
          ></image>
        </view>
        <view class="nav-icons">
          <view class="nav-button" @click="showRulesModal">
            <image
              class="nav-icon-img"
              :src="getImageUrl('资源 23-8.png')"
              mode="aspectFit"
            ></image>
            <text class="nav-button-text">规则</text>
          </view>
          <view class="nav-button" @click="showPrizeModal">
            <image
              class="nav-icon-img"
              :src="getImageUrl('资源 22-8.png')"
              mode="aspectFit"
            ></image>
            <text class="nav-button-text">奖品</text>
          </view>
        </view>
      </view>

      <!-- 路径插图 -->
      <view class="path-illustration">
        <image
          class="path-bg"
          :src="getImageUrl('资源 18-8.png')"
          mode="widthFix"
        ></image>
      </view>
    </view>

    <!-- 用户信息和统计区域 -->
    <view class="user-section">
      <!-- 左中右布局的蓝色卡片 -->
      <view class="user-card">
        <!-- 左侧：当前公益总步数 -->
        <view class="stats-left">
          <text class="stats-label">当前公益总步数</text>
          <text class="stats-value">{{ formattedprojectTotalStep }}</text>
        </view>

        <!-- 中间：用户头像（突出显示） -->
        <view class="user-profile">
          <view
            class="avatar-container avatar-clickable"
            @click="handleAvatarClick"
          >
            <image
              v-if="userInfo.avatarUrl"
              class="user-avatar"
              :src="userInfo.avatarUrl"
              mode="aspectFill"
            ></image>
            <view v-else class="temp-avatar">
              <text class="avatar-text">头像</text>
            </view>
          </view>
          <text class="user-id nickname-clickable" @click="handleNicknameClick"
            >ID: {{ userInfo.nickname || "未登录" }}</text
          >
        </view>

        <!-- 右侧：个人公益总步数 -->
        <view class="stats-right">
          <text class="stats-label">个人公益总步数</text>
          <text class="stats-value">{{ formattedPersonalTotalSteps }}</text>
        </view>
      </view>
    </view>

    <!-- 功能按钮区域 -->
    <view class="action-buttons">
      <!-- 爱心基金展示区域 -->
      <view class="charity-fund">
        <view class="fund-label-container">
          <view class="fund-label" :class="{ 'fund-label-scroll': needScroll }">
            <text class="fund-label-text-inner">参与捐步，江苏体彩将匹配捐赠"体彩爱心基金"，用于支持省委驻射阳县乡村振兴工作队开展公益活动。</text>
            <text class="fund-label-text-inner" v-if="needScroll">参与捐步，江苏体彩将匹配捐赠"体彩爱心基金"，用于支持省委驻射阳县乡村振兴工作队开展公益活动。</text>
          </view>
        </view>
        <!-- 爱心基金数据展示容器 -->
        <view class="fund-data-container">
          <image
            class="fund-background"
            :src="getImageUrl('资源 25-8.png')"
            mode="widthFix"
          ></image>
          <!-- 外层：负责匀速移动 -->
          <view
            class="boy-outer"
            :class="{
              'boy-moving': isAnimating,
              'boy-arrived': hasInitialized,
            }"
            :style="{
              left: (!hasInitialized ? startOffset : boyPosition) + '%',
            }"
          >
            <!-- 内层：负责摇晃（只在移动过程中摇晃） -->
            <image
              class="boy-inner boy-shaking"
              :src="getImageUrl('资源 24-8.png')"
              mode="widthFix"
            ></image>
          </view>
          <view class="current-fund">
            <text class="fund-label-text">当前爱心基金</text>
            <text class="fund-amount">{{ currentFundText }}元</text>
          </view>
          <view class="target-fund">
            <text class="fund-label-text">目标爱心基金</text>
            <text class="fund-amount">{{ targetFundText }}元</text>
          </view>
        </view>
      </view>

      <view class="primary-buttons">
        <image
          class="image-button"
          :src="getImageUrl('资源 13-8.png')"
          mode="widthFix"
          @click="goMusic"
        ></image>
        <image
          class="image-button"
          :src="getImageUrl('资源 14-8.png')"
          mode="widthFix"
          @click="goStore"
        ></image>
        <image
          class="image-button"
          :src="getImageUrl('资源 15-8.png')"
          mode="widthFix"
          @click="goPeripheral"
        ></image>
      </view>

      <view class="donation-buttons">
        <button class="donation-btn donation-btn-personal" @click="goPersonal">
          个人公益捐步
        </button>
        <button class="donation-btn" @click="goTeam">团队公益捐步</button>
      </view>
    </view>

    <!-- 活动时间信息 -->
    <view class="event-info">
      <image
        class="event-time-image"
        :src="getImageUrl('资源 8-8.png')"
        mode="widthFix"
      ></image>
      <view
        class="offline-registration-btn"
        @click="goOfflineRegistration"
      >
        <text class="offline-registration-text">线下徒步报名 ＞</text>
      </view>
    </view>

    <!-- 规则弹窗 -->
    <view class="modal-overlay" v-if="showRules" @click="hideRulesModal">
      <view class="modal-outer" @click.stop>
        <!-- 外层：标题和渐变色背景 -->
        <view class="modal-header-outer">
          <text class="modal-title-outer">线上活动规则</text>
        </view>

        <!-- 内层：具体内容和按钮 -->
        <view class="modal-inner">
          <!-- 内容区域 -->
          <scroll-view class="modal-content-scroll" scroll-y="true">
            <view class="modal-body">
              <view class="rule-item">
                <text class="rule-title">一、活动时间</text>
                <text class="rule-detail"
                  >2025年11月17日至11月30日，分两周开展。\n第一周：11月17日—11月23日\n第二周：11月24日—11月30日</text
                >
              </view>
              <view class="rule-item">
                <text class="rule-title">二、活动人群</text>
                <text class="rule-detail"
                  >江苏省内年满18周岁，且已授权绑定"微信运动"功能的用户。</text
                >
              </view>
              <view class="rule-item">
                <text class="rule-title">三、活动细则</text>
                <text class="rule-detail"
                  >（一）参与方式\n参与者通过关注江苏体彩微信订阅号，点击"爱行走"菜单，进入活动页面，每日9:00-23:00间上传微信运动步数。\n\n（二）兑换规则\n1.用户参与活动每捐赠1万步，江苏省体彩中心将匹配捐赠5元"体彩爱心基金"。\n2."体彩爱心基金"总金额20万元封顶。满20万元后，爱心基金总额不再增加，所有活动版块可继续参与。\n3.本次"体彩爱心基金"将捐赠给中共江苏省委驻射阳县乡村振兴工作队用于开展公益活动。\n\n（三）活动玩法\n本次线上活动设4个模块，以"公益捐步"为核心。参与者完成"公益捐步"后，可自主参与"门店打卡""行走的歌单""周边领取"三项叠加任务。\n\n（四）活动模块\n活动一：公益捐步\n1.个人公益捐步\n（1）每日通过点击首页"个人公益捐步"按钮，调取实时微信运动数据上传至平台。\n（2）每日可多次上传运动数据，每人每天最高可捐赠3万步。\n（3）单日捐步≥3000步，可参与QQ音乐豪华绿钻会员周卡抽奖；单日捐步≥8000步，可参与QQ音乐豪华绿钻会员月卡抽奖。\n（4）单个活动周期内，完成7天捐步的用户可参与50元京东E卡抽奖，并获得爱行走电子证书。\n2.团队捐步\n（1）用户每周可邀请微信好友，组成3人小队；第一周活动结束，小队自动解散，第二周需重新组队。\n（2）小队步数为成员个人步数之和，数据实时更新。\n（3）每周小队步数达到5万步的，小队每位成员可参与QQ音乐豪华绿钻会员季卡抽奖。\n（4）用户可在"团队公益捐步"实时查看本周成团状态、团队成员捐步次数及周期任务完成进度。\n\n活动二：门店打卡\n1.玩法介绍\n点击首页"门店打卡"，每日查看附近体彩门店且打卡成功，可获得一次抽奖机会。\n2.奖品设置\n（1）QQ音乐豪华绿钻会员卡，周卡、月卡或季卡。\n（2）10-50元价值不等的京东E卡。\n\n活动三：行走的歌单\n1.玩法介绍\n点击首页"行走的歌单"，每日选择关键词后生成"我的专属歌单"，保存分享歌单，可获得一次抽奖机会。\n2.奖品设置\n（1）QQ音乐豪华绿钻会员卡，周卡、月卡或季卡。\n（2）10-50元价值不等的京东E卡。\n\n活动四：周边领取\n凡在2025年11月17日至11月30日期间，前往以下江苏省内指定体彩门店，关注江苏体彩微信订阅号，参与捐步即有机会免费领取第十一季体彩爱行走创意冰箱贴。每个用户仅可领取1个，限量发行，先到先得。\n冰箱贴领取网点地址一览表如下：</text
                >
                <view class="rule-table">
                  <view class="rule-table-row rule-table-header">
                    <text class="rule-table-cell code">网点号</text>
                    <text class="rule-table-cell address">网点地址</text>
                    <text class="rule-table-cell color">可领冰箱贴颜色</text>
                  </view>
                  <view
                    class="rule-table-row"
                    v-for="(store, index) in iceboxStores"
                    :key="store.code || index"
                  >
                    <text class="rule-table-cell code">{{ store.code }}</text>
                    <text class="rule-table-cell address">{{ store.address }}</text>
                    <text class="rule-table-cell color">{{ store.color }}</text>
                  </view>
                </view>
                <text class="rule-detail"
                  >（五）奖品发放\n1.QQ音乐豪华绿钻会员：中奖者点击活动首页"奖品"进行查看，需前往QQ音乐APP兑换（点击【会员】-【其他开通方式】-【兑换VIP】-输入兑换码及验证码-点击【立即兑换】），QQ音乐豪华绿钻会员使用有效期以QQ音乐APP显示为准。QQ音乐豪华绿钻会员领取时间截止到2025年12月03日，逾期未领取视为自动放弃领取奖品权益，请务必在此之前完成兑换流程。\n2.京东E卡：中奖者点击活动首页"奖品"进行查看，需前往京东APP兑换（点击【我的】-【京东E卡】-【绑定新卡】- 输入卡密并通过验证 - 点击【确认绑定】），京东E卡使用有效期以京东app显示为准。京东E卡领取时间截止到2025年12月03日，逾期未领取视为自动放弃领取奖品权益，请务必在此之前完成绑定流程。\n3.如发现用户在参与活动中存在技术作弊等问题，主办方有权取消该用户参与本次活动的资格。\n\n（六）咨询方式\n活动统一咨询电话：025-86896060\n（工作时间：周一至周五 9:00—12:00，14:00—17:00）</text
                >
              </view>
            </view>
          </scroll-view>

          <!-- 底部按钮 -->
          <view class="modal-footer">
            <button class="modal-btn" @click="hideRulesModal">我已知晓</button>
          </view>
        </view>
      </view>
    </view>

    <!-- 用户授权弹窗 -->
    <view class="auth-modal-overlay" v-if="showAuthModal" @click.stop>
      <view class="auth-modal-container" @click.stop>
        <view class="auth-modal-header">
          <text class="auth-modal-title">用户授权</text>
          <view class="auth-modal-close" @click="handleAuthCancel">
            <text class="close-icon">×</text>
          </view>
        </view>
        <view class="auth-modal-body">
          <text class="auth-modal-desc"
            >为了给您提供更好的服务，需要完善您的基本信息</text
          >

          <!-- 新版本头像昵称填写能力（基础库 >= 2.21.2） -->
          <!-- #ifdef MP-WEIXIN -->
          <view v-if="supportNewUserInfo" class="new-user-info-form">
            <view class="user-info-form-item">
              <text class="form-label">头像</text>
              <button
                class="avatar-button"
                open-type="chooseAvatar"
                @chooseavatar="onChooseAvatar"
              >
                <image
                  v-if="tempAvatarUrl"
                  class="avatar-preview"
                  :src="tempAvatarUrl"
                  mode="aspectFill"
                ></image>
                <view v-else class="avatar-placeholder">
                  <text>点击选择头像</text>
                </view>
              </button>
            </view>
            <view class="user-info-form-item">
              <text class="form-label">昵称</text>
              <input
                class="nickname-input"
                type="nickname"
                placeholder="请输入昵称"
                :value="tempNickname"
                @input="onNicknameInput"
                @blur="onNicknameBlur"
              />
            </view>
            <view class="user-info-form-item">
              <text class="form-label">手机号</text>
              <view class="phone-input-wrapper">
                <input
                  class="nickname-input phone-input"
                  type="number"
                  placeholder="请输入手机号码"
                  :value="tempMobile"
                  @input="onMobileInput"
                  @blur="onMobileBlur"
                  placeholder-style="color: #CCCCCC"
                />
                <!-- #ifdef MP-WEIXIN -->
                <button
                  class="wechat-auth-btn"
                  open-type="getPhoneNumber"
                  @getphonenumber="handleGetPhoneNumber"
                >
                  微信获取
                </button>
                <!-- #endif -->
                <!-- #ifndef MP-WEIXIN -->
                <text class="wechat-auth-text" @click="getWechatPhone"
                  >微信获取</text
                >
                <!-- #endif -->
              </view>
            </view>
          </view>
          <!-- #endif -->

          <!-- 旧版本提示（基础库 < 2.21.2） -->
          <!-- #ifdef MP-WEIXIN -->
          <view v-else class="auth-info-list">
            <view class="auth-info-item">
              <view class="auth-info-icon">✓</view>
              <text class="auth-info-text">获取您的微信头像和昵称</text>
            </view>
            <view class="auth-info-item">
              <view class="auth-info-icon">✓</view>
              <text class="auth-info-text">获取您的基本信息用于活动参与</text>
            </view>
          </view>
          <!-- #endif -->

          <!-- 非微信小程序环境 -->
          <!-- #ifndef MP-WEIXIN -->
          <view class="auth-info-list">
            <view class="auth-info-item">
              <view class="auth-info-icon">✓</view>
              <text class="auth-info-text">获取您的基本信息用于活动参与</text>
            </view>
          </view>
          <!-- #endif -->
        </view>
        <view class="auth-modal-footer">
          <button class="auth-btn auth-btn-cancel" @click="handleAuthCancel">
            暂不授权
          </button>
          <button class="auth-btn auth-btn-confirm" @click="handleAuthConfirm">
            同意授权
          </button>
        </view>
      </view>
    </view>

    <!-- 奖品弹窗 -->
    <view class="prize-modal-overlay" v-if="showPrize" @click="hidePrizeModal">
      <view class="prize-modal-container" @click.stop>
        <!-- 头部固定区域 -->
        <view class="prize-modal-header">
          <!-- 标题 -->
          <text class="prize-modal-title">我的奖品</text>
          <!-- 关闭按钮 -->
          <view class="prize-modal-close" @click="hidePrizeModal">
            <image
              class="close-icon-image"
              :src="getImageUrl('资源 1-8.png')"
              mode="aspectFit"
            ></image>
          </view>
        </view>

        <!-- 标签页导航 -->
        <view class="prize-tabs">
          <view
            class="prize-tab"
            :class="{ 'prize-tab-active': selectedPrizeWeek === 1 }"
            @click="selectedPrizeWeek = 1"
          >
            <text class="prize-tab-text">第一周奖品</text>
          </view>
          <view
            class="prize-tab"
            :class="{ 'prize-tab-active': selectedPrizeWeek === 2 }"
            @click="selectedPrizeWeek = 2"
          >
            <text class="prize-tab-text">第二周奖品</text>
          </view>
        </view>

        <!-- 滚动内容区域 -->
        <scroll-view
          class="prize-modal-content"
          scroll-y="true"
          enable-flex="true"
        >
          <view class="prize-list">
            <!-- 有奖品时显示列表 -->
            <template v-if="filteredPrizes.length > 0">
              <view
                class="prize-card"
                v-for="(prize, index) in filteredPrizes"
                :key="index"
              >
                <view class="prize-card-left">
                  <text class="prize-name">{{ prize.name }}</text>
                </view>
                <view class="prize-card-right">
                  <button
                    class="prize-action-btn"
                    :class="{
                      'prize-action-btn-disabled': prize.status === 'claimed',
                    }"
                    @click="handlePrizeAction(prize)"
                  >
                    {{ prize.actionText }}
                  </button>
                </view>
              </view>
            </template>
            <!-- 无奖品时显示空状态提示 -->
            <view class="prize-empty" v-else>
              <text class="prize-empty-text">暂无奖品</text>
              <text class="prize-empty-hint">奖品需要抽奖才有机会获取哦~</text>
            </view>
          </view>
        </scroll-view>

        <!-- 底部抽奖按钮区域 -->
        <view class="prize-modal-footer">
          <view
            class="lottery-button-in-modal"
            @click="handleLotteryFromPrizeModal"
            :class="{ 'lottery-button-active': lotteryTimes > 0 }"
          >
            <image
              class="lottery-icon-in-modal"
              :src="getImageUrl('资源 22-8.png')"
              mode="aspectFit"
            ></image>
            <text class="lottery-text-in-modal"
              >剩余抽奖次数：{{ lotteryTimes }}</text
            >
          </view>
        </view>
      </view>
    </view>

    <!-- 是否和好友组队弹窗 -->
    <view v-if="showTeamJoinModal" class="team-modal-overlay" @click.stop>
      <view class="team-modal-container" @click.stop>
        <view class="team-modal-header">
          <text class="team-modal-title">邀请组队</text>
        </view>
        <view class="team-modal-body">
          <text class="team-modal-text">是否和好友组队？</text>
        </view>
        <view class="team-modal-footer">
          <button
            class="team-modal-btn team-modal-btn-cancel"
            @click="cancelJoinTeam"
          >
            否
          </button>
          <button
            class="team-modal-btn team-modal-btn-confirm"
            @click="confirmJoinTeam"
          >
            是
          </button>
        </view>
      </view>
    </view>

    <!-- 期待您的加入弹窗 -->
    <view
      v-if="showTeamWelcomeModal"
      class="team-modal-overlay"
      @click="closeWelcomeModal"
    >
      <view class="team-modal-container" @click.stop>
        <view class="team-modal-header">
          <text class="team-modal-title">期待您的加入</text>
        </view>
        <view class="team-modal-body">
          <text class="team-modal-text"
            >期待您的加入！您可以自己组队，邀请好友一起参与团队公益捐步活动，共同为公益事业贡献力量。</text
          >
        </view>
        <view class="team-modal-footer">
          <button class="team-modal-btn" @click="closeWelcomeModal">
            我知道了
          </button>
        </view>
      </view>
    </view>

    <!-- 快去和别人组队吧弹窗 -->
    <view
      v-if="showTeamGoInviteModal"
      class="team-modal-overlay"
      @click="closeGoInviteModal"
    >
      <view class="team-modal-container" @click.stop>
        <view class="team-modal-header">
          <text class="team-modal-title">提示</text>
        </view>
        <view class="team-modal-body">
          <text class="team-modal-text">快去和别人组队吧</text>
        </view>
        <view class="team-modal-footer">
          <button class="team-modal-btn" @click="closeGoInviteModal">
            我知道了
          </button>
        </view>
      </view>
    </view>

    <!-- 是否加入该团队弹窗 -->
    <view
      v-if="showTeamJoinConfirmModal"
      class="success-modal-mask"
      @click="cancelJoinTeamFromConfirm"
    >
      <view class="success-modal-content" @click.stop>
        <!-- 关闭按钮 -->
        <view class="close-button" @click="cancelJoinTeamFromConfirm">
          <image
            class="close-icon"
            src="/static/资源 1-8.png"
            mode="aspectFit"
          ></image>
        </view>
        <!-- 背景图片容器 -->
        <view class="success-modal-bg-wrapper">
          <image
            class="success-modal-bg"
            src="/static/box.png"
            mode="aspectFit"
            :style="{ width: '600rpx', height: '800rpx' }"
          ></image>
          <!-- 吉祥物图片 -->
          <image
            class="success-modal-mascot"
            :src="getModalMascotImage('是')"
            mode="aspectFit"
          ></image>
          <!-- 提示文字 -->
          <view
            class="success-text small"
            :style="{ color: getModalTextColor('是') }"
          >
            是否加入该团队
          </view>
          <!-- 按钮容器 -->
          <view class="confirm-buttons-container">
            <view
              class="confirm-button-cancel"
              @click="cancelJoinTeamFromConfirm"
            >
              否
            </view>
            <view
              class="confirm-button-confirm"
              @click="confirmJoinTeamFromConfirm"
            >
              是
            </view>
          </view>
        </view>
      </view>
    </view>

    <!-- 组队成功弹窗 -->
    <view
      v-if="showTeamJoinSuccessModal"
      class="success-modal-mask"
      @click="closeTeamJoinSuccessModal"
    >
      <view class="success-modal-content" @click.stop>
        <!-- 关闭按钮 -->
        <view class="close-button" @click="closeTeamJoinSuccessModal">
          <image
            class="close-icon"
            src="/static/资源 1-8.png"
            mode="aspectFit"
          ></image>
        </view>
        <!-- 背景图片容器 -->
        <view class="success-modal-bg-wrapper">
          <image
            class="success-modal-bg"
            src="/static/box.png"
            mode="aspectFit"
            :style="{ width: '600rpx', height: '800rpx' }"
          ></image>
          <!-- 吉祥物图片 -->
          <image
            class="success-modal-mascot"
            :src="getModalMascotImage('立即查看')"
            mode="aspectFit"
          ></image>
          <!-- 提示文字 -->
          <view
            class="success-text small"
            :style="{ color: getModalTextColor('立即查看') }"
          >
            恭喜你加入团队成功
          </view>
          <!-- 立即查看按钮 -->
          <view class="lottery-button" @click="goToTeamIndex"> 立即查看 </view>
        </view>
      </view>
    </view>

    <!-- 你已经加入过团队弹窗 -->
    <view
      v-if="showTeamAlreadyJoinedModal"
      class="success-modal-mask"
      @click="closeTeamAlreadyJoinedModal"
    >
      <view class="success-modal-content" @click.stop>
        <!-- 关闭按钮 -->
        <view class="close-button" @click="closeTeamAlreadyJoinedModal">
          <image
            class="close-icon"
            src="/static/资源 1-8.png"
            mode="aspectFit"
          ></image>
        </view>
        <!-- 背景图片容器 -->
        <view class="success-modal-bg-wrapper">
          <image
            class="success-modal-bg"
            src="/static/box.png"
            mode="aspectFit"
            :style="{ width: '600rpx', height: '800rpx' }"
          ></image>
          <!-- 吉祥物图片 -->
          <image
            class="success-modal-mascot"
            :src="getModalMascotImage('点击查看')"
            mode="aspectFit"
          ></image>
          <!-- 提示文字 -->
          <view
            class="success-text small"
            :style="{ color: getModalTextColor('点击查看') }"
          >
            你已经加入过团队
          </view>
          <!-- 点击查看按钮 -->
          <view class="lottery-button" @click="goToTeamIndexFromAlreadyJoined">
            点击查看
          </view>
        </view>
      </view>
    </view>

    <!-- 需要先去捐步弹窗 -->
    <view
      v-if="showNoStepsModal"
      class="success-modal-mask"
      @click="closeNoStepsModal"
    >
      <view class="success-modal-content" @click.stop>
        <!-- 关闭按钮 -->
        <view class="close-button" @click="closeNoStepsModal">
          <image
            class="close-icon"
            src="/static/资源 1-8.png"
            mode="aspectFit"
          ></image>
        </view>
        <!-- 背景图片容器 -->
        <view class="success-modal-bg-wrapper">
          <image
            class="success-modal-bg"
            src="/static/box.png"
            mode="aspectFit"
            :style="{ width: '600rpx', height: '800rpx' }"
          ></image>
          <!-- 吉祥物图片 -->
          <image
            class="success-modal-mascot"
            :src="getModalMascotImage('立即捐步')"
            mode="aspectFit"
          ></image>
          <!-- 提示文字 -->
          <view
            class="success-text small"
            :style="{ color: getModalTextColor('立即捐步') }"
          >
            <text>完成捐步</text>
            <text>即可解锁该功能</text>
          </view>
          <!-- 去捐步按钮 -->
          <view class="lottery-button" @click="goToDonation"> 立即捐步 </view>
        </view>
      </view>
    </view>

    <!-- 线下活动已报满弹窗 -->
    <view
      v-if="showOfflineFullModal"
      class="success-modal-mask"
      @click="closeOfflineFullModal"
    >
      <view class="success-modal-content" @click.stop>
        <!-- 关闭按钮 -->
        <view class="close-button" @click="closeOfflineFullModal">
          <image
            class="close-icon"
            src="/static/资源 1-8.png"
            mode="aspectFit"
          ></image>
        </view>
        <!-- 背景图片容器 -->
        <view class="success-modal-bg-wrapper">
          <image
            class="success-modal-bg"
            src="/static/box.png"
            mode="aspectFit"
            :style="{ width: '600rpx', height: '800rpx' }"
          ></image>
          <!-- 吉祥物图片 -->
          <image
            class="success-modal-mascot"
            :src="getModalMascotImage('知道了')"
            mode="aspectFit"
          ></image>
          <!-- 提示文字 -->
          <view
            class="success-text small"
            :style="{ color: getModalTextColor('知道了') }"
          >
          线下徒步报名已满
            <text>感谢关注与支持</text>
          </view>
          <!-- 我知道了按钮 -->
          <view class="lottery-button" @click="closeOfflineFullModal">
            知道了
          </view>
        </view>
      </view>
    </view>

    <!-- 线下活动已结束弹窗 -->
    <view
      v-if="showOfflineEndModal"
      class="success-modal-mask"
      @click="closeOfflineEndModal"
    >
      <view class="success-modal-content" @click.stop>
        <!-- 关闭按钮 -->
        <view class="close-button" @click="closeOfflineEndModal">
          <image
            class="close-icon"
            src="/static/资源 1-8.png"
            mode="aspectFit"
          ></image>
        </view>
        <!-- 背景图片容器 -->
        <view class="success-modal-bg-wrapper">
          <image
            class="success-modal-bg"
            src="/static/box.png"
            mode="aspectFit"
            :style="{ width: '600rpx', height: '800rpx' }"
          ></image>
          <!-- 吉祥物图片 -->
          <image
            class="success-modal-mascot"
            :src="getModalMascotImage('知道了')"
            mode="aspectFit"
          ></image>
          <!-- 提示文字 -->
          <view
            class="success-text small"
            :style="{ color: getModalTextColor('知道了') }"
          >
            线下徒步报名已结束
            <text>感谢关注与支持</text>
          </view>
          <!-- 我知道了按钮 -->
          <view class="lottery-button" @click="closeOfflineEndModal">
            知道了
          </view>
        </view>
      </view>
    </view>

    <!-- 手机号弹窗 -->
    <view
      v-if="showPhoneModal"
      class="phone-modal-overlay"
      @click.stop
    >
      <view class="phone-modal-container" @click.stop>
        <view class="phone-modal-header">
          <text class="phone-modal-title">需要手机号</text>
          <view class="phone-modal-close" @click="handlePhoneCancel">
            <text class="close-icon">×</text>
          </view>
        </view>
        <view class="phone-modal-body">
          <text class="phone-modal-desc"
            >为了给您提供更好的服务，需要完善您的手机号信息</text
          >
          <view class="phone-form-item">
            <text class="form-label">手机号</text>
            <view class="phone-input-wrapper">
              <input
                class="phone-form-input"
                type="number"
                placeholder="请输入手机号码"
                :value="phoneModalMobile"
                @input="onPhoneModalInput"
                @blur="onPhoneModalBlur"
                placeholder-style="color: #CCCCCC"
              />
              <!-- #ifdef MP-WEIXIN -->
              <button
                class="wechat-auth-btn"
                open-type="getPhoneNumber"
                @getphonenumber="handlePhoneModalGetPhoneNumber"
              >
                微信获取
              </button>
              <!-- #endif -->
              <!-- #ifndef MP-WEIXIN -->
              <text class="wechat-auth-text" @click="getWechatPhone"
                >微信获取</text
              >
              <!-- #endif -->
            </view>
          </view>
        </view>
        <view class="phone-modal-footer">
          <button class="phone-btn phone-btn-cancel" @click="handlePhoneCancel">
            暂不填写
          </button>
          <button class="phone-btn phone-btn-confirm" @click="handlePhoneConfirm">
            同意授权
          </button>
        </view>
      </view>
    </view>

    <!-- 证书查看弹窗 -->
    <view
      v-if="showCertificateModal"
      class="certificate-modal-overlay"
      @click="closeCertificateModal"
    >
      <view class="certificate-modal-container" @click.stop>
        <!-- 关闭按钮 -->
        <view class="certificate-close" @click="closeCertificateModal">
          <image
            class="certificate-close-icon"
            src="/static/资源 1-8.png"
            mode="aspectFit"
          ></image>
        </view>
        <!-- 证书图片 -->
        <scroll-view class="certificate-content" scroll-y="true">
          <image
            class="certificate-image"
            :src="certificateDisplayUrl"
            mode="widthFix"
            @error="onCertificateImageError"
          ></image>
        </scroll-view>
        <!-- 保存按钮 -->
        <view class="certificate-footer">
          <button class="certificate-save-btn" @click="saveCertificate">
            保存到相册
          </button>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
import { code2Session, login, hasValidOpenID, updateUserProfile } from "@/api";
import { joinTeam } from "@/api/team";
import { getImageUrl, IMAGE_BASE_URL } from "@/api/config";
import { addLotteryChance } from "@/api/lottery";
import { SpbLottryType } from "@/api/apifox/models";
import { decryptPhoneNumber as decryptPhoneFromAPI } from "@/api/user";
import { userStore } from "@/api/userStore";

export default {
  data() {
    return {
      showRules: false,
      showPrize: false,
      showAuthModal: false, // 授权弹窗显示状态
      // 用户信息
      userInfo: {
        openid: "",
        nickname: "",
        avatarUrl: "",
        gender: 0,
        country: "",
        province: "",
        city: "",
        language: "",
      },
      // 临时存储的头像和昵称（用于新版本填写能力）
      tempAvatarUrl: "",
      tempNickname: "",
      tempMobile: "", // 临时存储的手机号
      // 基础库版本
      baseLibVersion: "",
      // 步数数据
      projectTotalStep: 0, // 当前公益总步数
      personalTotalSteps: 0, // 个人公益总步数
      todaySteps: 0, // 今日捐步数
      // 爱心基金数据（单位：元）
      currentFund: 0, // 当前基金，从接口获取
      targetFund: 200000, // 目标基金：20万
      isAnimating: false, // 初始化动画状态
      hasInitialized: false, // 是否已完成初始化
      containerWidthRpx: 0, // 容器宽度（rpx）
      animationDistanceRpx: 0, // 动画移动距离（rpx）
      // 奖品数据
      prizes: [],
      // 抽奖次数
      lotteryTimes: 0,
      // 线下报名是否已满
      isOfflineFull: false,
      showOfflineFullModal: false,
      // 线下报名是否已结束
      showOfflineEndModal: false,
      // 证书弹窗
      showCertificateModal: false,
      certificateUrl:
        "https://hykoc.oss-cn-beijing.aliyuncs.com/koc/tc2025/certification3.jpg",
      certificateDisplayUrl:
        "https://hykoc.oss-cn-beijing.aliyuncs.com/koc/tc2025/certification3.jpg",
      // 当前周次（从 login 接口获取，1: 第一周, 2: 第二周）
      currentWeek: 1,
      // 奖品弹窗选中的周次（1: 第一周, 2: 第二周）
      selectedPrizeWeek: 1,
      // 团队相关
      shareTeamId: null, // 从URL参数获取的teamId
      showTeamAlreadyInModal: false, // 显示"你已经加入此队"弹窗
      showTeamDifferentModal: false, // 显示"已经加入别的队伍"弹窗
      showTeamJoinModal: false, // 显示"是否和好友组队"弹窗
      showTeamWelcomeModal: false, // 显示"期待您的加入"弹窗
      showTeamGoInviteModal: false, // 显示"快去和别人组队吧"弹窗
      showTeamJoinSuccessModal: false, // 显示"组队成功"弹窗
      showTeamAlreadyJoinedModal: false, // 显示"你已经加入过团队"弹窗
      showTeamJoinConfirmModal: false, // 显示"是否加入该团队"弹窗
      showNoStepsModal: false, // 显示"需要先去捐步"弹窗
      needScroll: false, // 文案是否需要滚动
      showPhoneModal: false, // 显示手机号弹窗
      phoneModalMobile: "", // 手机号弹窗中的手机号
      iceboxStores: [
        { code: "3201288182", address: "南京市中山门大街699号招商花园城加铺-2F", color: "红色" },
        { code: "3201289207", address: "南京市地铁1号线新街口站C29号商铺", color: "绿色" },
        { code: "3201289768", address: "南京市中和路68号金陵天地4楼体育彩票", color: "黄色" },
        { code: "3202022779", address: "江阴市澄江街道绮山路223号", color: "红色" },
        { code: "3202002669", address: "宜兴市宜城街道人民南路226号球迷俱乐部", color: "绿色" },
        { code: "3202022180", address: "无锡市滨湖区大通路尊鹏云著3-1号店铺", color: "黄色" },
        { code: "3203289133", address: "徐州市贾汪区中旺路1号吾悦广场1F", color: "红色" },
        { code: "3203289388", address: "徐州市鼓楼区复兴北路27号宝龙广场1F中国体育彩票", color: "绿色" },
        { code: "3203288640", address: "徐州市云龙区和平大道58号", color: "黄色" },
        { code: "3204024474", address: "常州市钟楼区吾悦广场负一层展示体验中心（名创优品旁）", color: "红色" },
        { code: "3204054446", address: "常州市奥林匹克体育中心E37口体彩网点", color: "绿色" },
        { code: "3204004012", address: "常州市常州文化广场体彩网点（近幸福蓝海氿龙国际影城）", color: "黄色" },
        { code: "3205055258", address: "苏州市姑苏区观前街宫巷75号", color: "红色" },
        { code: "3205055204", address: "苏州市工业园区奥体商业中心负一楼", color: "绿色" },
        { code: "3205005302", address: "苏州市高新区竹园路9-2号上贤雅苑社区用房一楼大厅南侧", color: "黄色" },
        { code: "3206056372", address: "南通市崇川区环城东路13号附6号", color: "红色" },
        { code: "3206056659", address: "启东市长龙街633-635号门面", color: "绿色" },
        { code: "3206056399", address: "南通市崇川区尚海湾尚文路附4幢109室", color: "黄色" },
        { code: "3207007121", address: "连云港市海州区中大街81号", color: "红色" },
        { code: "3207007327", address: "连云港市海州区香溢世纪花城南门7号门面中国体育彩票", color: "绿色" },
        { code: "3207007302", address: "连云港市连云区院前路3号", color: "黄色" },
        { code: "3208028301", address: "淮安市开发区珠海路105号1幢106珠海小区旁", color: "红色" },
        { code: "3208028103", address: "淮安市清江浦区开元路淮海第一城南屏苑商铺25室", color: "绿色" },
        { code: "3208028203", address: "淮安市清江浦北京路众城名府12-6号", color: "黄色" },
        { code: "3209059082", address: "盐城市亭湖区民航新村锦康苑102门市", color: "红色" },
        { code: "3209059786", address: "盐城市中南城魔力月光10151门市", color: "绿色" },
        { code: "3209029080", address: "盐城市经济开发区阳光世纪园西大门中国体育彩票", color: "黄色" },
        { code: "3210030071", address: "扬州市邗江区文昌中路606号万象汇B108", color: "红色" },
        { code: "3210289131", address: "扬州市邗江区扬子津北路101号印象汇1F", color: "绿色" },
        { code: "3210288377", address: "扬州市邗江区邗江中路302号万达广场4L", color: "黄色" },
        { code: "3211061053", address: "镇江市京口区纬四路16号吾悦广场4楼", color: "红色" },
        { code: "3211061037", address: "镇江市中山东路 301号苏宁广场B1楼", color: "绿色" },
        { code: "3211288979", address: "镇江市黄山西路19号万达广场3楼", color: "黄色" },
        { code: "3212062120", address: "泰州市海陵区迎宾路188-41号", color: "红色" },
        { code: "3212032030", address: "泰州市高港区西榭路华泽天下花园22栋103室", color: "绿色" },
        { code: "3212032037", address: "泰州市海陵区济川东路美好上郡南门202号", color: "黄色" },
        { code: "3213271593", address: "宿迁市宿城区宝龙广场一楼", color: "红色" },
        { code: "3213272133", address: "宿迁市宿城区吾悦广场1F2号门", color: "绿色" },
        { code: "3213288646", address: "宿迁市宿豫区项王东路8号万达广场1楼", color: "黄色" },
      ],
    };
  },
  computed: {
    // 计算进度百分比（0-1）
    progress() {
      if (this.targetFund <= 0) return 0;
      return Math.min(this.currentFund / this.targetFund, 1);
    },
    // 左边起始位置百分比（用于动画）
    startOffset() {
      return 5; // 左边起始位置百分比
    },
    // 计算小人位置（百分比，考虑左右边距，让小人不超出跑道）
    boyPosition() {
      // 跑道有效宽度百分比（假设左右各留5%的边距）
      const startOffset = 5; // 左边起始位置百分比
      const endOffset = 5; // 右边结束位置百分比
      const trackWidth = 100 - startOffset - endOffset; // 有效跑道宽度
      // 根据进度计算位置，从左边起始位置开始
      return startOffset + trackWidth * this.progress;
    },
    // 计算动画需要移动的距离（从最左边到目标位置，用于CSS变量）
    animationMoveDistance() {
      const startOffset = 5;
      const endOffset = 5;
      const trackWidth = 100 - startOffset - endOffset;
      const targetPosition = startOffset + trackWidth * this.progress;
      // 计算从最左边（5%）到目标位置需要移动的百分比
      return targetPosition - startOffset;
    },
    // 格式化步数显示（添加千位分隔符）
    formattedprojectTotalStep() {
      // 当步数达到或超过10万时，仅展示"万"为单位，保留整数部分，避免精度溢出
      const val = this.projectTotalStep;
      if (val === null || val === undefined || val === "") {
        return "0";
      }
      let n = 0;
      if (typeof val === "number") {
        if (!isFinite(val) || isNaN(val)) return "0";
        n = Math.floor(val);
      } else {
        // 字符串或其他类型，做健壮处理，避免大数精度问题（仅保留数字）
        const str = String(val).replace(/[^\d]/g, "");
        if (!str) return "0";
        n = parseInt(str, 10) || 0;
      }
      // 单位格式化：≥1亿显示“亿”，≥1万显示“万”，不保留小数，向下取整
      const formatUnit = (num, base, label) => {
        const v = Math.floor(num / base); // 向下取整为整数
        return `${v}${label}`;
      };
      if (n >= 100000000) {
        return formatUnit(n, 100000000, "亿");
      }
      if (n >= 10000) {
        return formatUnit(n, 10000, "万");
      }
      return String(n);
    },
    formattedPersonalTotalSteps() {
      const val = this.personalTotalSteps;
      if (val === null || val === undefined || val === "") {
        return "0";
      }
      if (typeof val === "number") {
        if (!isFinite(val) || isNaN(val)) return "0";
        return String(Math.floor(val));
      }
      const str = String(val).trim();
      const numeric = parseInt(str, 10);
      if (isNaN(numeric)) {
        return "0";
      }
      return String(numeric);
    },
    formattedTodaySteps() {
      return this.formatNumber(this.todaySteps);
    },
    // 当前基金显示文本
    currentFundText() {
      if (this.currentFund >= 10000) {
        return (this.currentFund / 10000).toFixed(2) + "万";
      }
      return this.currentFund + "";
    },
    // 目标基金显示文本
    targetFundText() {
      if (this.targetFund >= 10000) {
        return (this.targetFund / 10000).toFixed(2) + "万";
      }
      return this.targetFund + "";
    },
    // 判断是否支持新的头像昵称填写能力（基础库 >= 2.21.2）
    supportNewUserInfo() {
      // #ifdef MP-WEIXIN
      if (!this.baseLibVersion) {
        return false;
      }
      // 将版本号转换为数字进行比较，例如 "2.21.2" -> 2.21.2
      const versionParts = this.baseLibVersion.split(".");
      const major = parseInt(versionParts[0] || 0);
      const minor = parseInt(versionParts[1] || 0);
      const patch = parseInt(versionParts[2] || 0);

      // 版本 >= 2.21.2
      if (major > 2) return true;
      if (major === 2 && minor > 21) return true;
      if (major === 2 && minor === 21 && patch >= 2) return true;
      return false;
      // #endif
      // #ifndef MP-WEIXIN
      return false;
      // #endif
    },
    // 根据选中的周次过滤奖品
    filteredPrizes() {
      if (!Array.isArray(this.prizes) || this.prizes.length === 0) {
        return [];
      }
      // 按照奖品的 weekNumber 字段进行分周显示：'1' 为第一周，'2' 为第二周
      const targetWeek = this.selectedPrizeWeek === 2 ? "2" : "1";
      return this.prizes.filter((p) => {
        const wn = p?.originalData?.weekNumber;
        // weekNumber 可能是字符串或数字
        const normalized = wn !== undefined && wn !== null ? String(wn) : "1";
        return normalized === targetWeek;
      });
    },
  },
  watch: {
    // 监听授权弹窗显示状态，预填充或重置临时数据
    showAuthModal(newVal) {
      if (newVal) {
        // 弹窗显示时，如果有现有信息，预填充到临时数据中，方便用户修改
        // 如果没有现有信息，则清空临时数据
        this.tempAvatarUrl = this.userInfo.avatarUrl || "";
        this.tempNickname = this.userInfo.nickname || "";
        // 尝试从 userStore 获取手机号（如果有的话）
        try {
          const loginData = userStore.getLoginData();
          this.tempMobile = (loginData && loginData.phone) || "";
        } catch (error) {
          this.tempMobile = "";
        }
      }
    },
  },
  onLoad(options) {
    // 获取基础库版本
    // #ifdef MP-WEIXIN
    const systemInfo = uni.getSystemInfoSync();
    this.baseLibVersion = systemInfo.SDKVersion || "";
    console.log("基础库版本:", this.baseLibVersion);
    // #endif

    // 检查URL参数中是否有teamId
    if (options && options.teamId) {
      this.shareTeamId = options.teamId;
      console.log("检测到teamId参数:", this.shareTeamId);
    }

    // 如果用户已登录，获取用户信息并检查手机号
    // 注意：刚进入时如果没有openid，不要直接弹出授权弹窗
    if (hasValidOpenID()) {
      this.fetchUserInfo();
    }
  },
  onReady() {
    // 页面渲染完成后触发初始化动画
    this.$nextTick(() => {
      this.startInitialAnimation();
      // 检测文案是否需要滚动
      this.checkFundLabelScroll();
    });
  },
  async onShow() {
    // 页面显示时刷新抽奖次数（从抽奖页面返回时更新）
    if (hasValidOpenID()) {
      await this.refreshLotteryTimes();
      // 同时检查手机号（可能在返回时用户信息已更新）
      const loginData = userStore.getLoginData();
      if (loginData) {
        this.checkPhoneAndShowModal(loginData);
      }
    }
  },
  methods: {
    getImageUrl,
    ensureAuthorized() {
      if (hasValidOpenID()) {
        if (!this.userInfo.openid) {
          this.fetchUserInfo();
        }
        return true;
      }
      this.showAuthModal = true;
      return false;
    },
    // 格式化数字（添加千位分隔符）
    // 安全处理各种输入类型：字符串、数字、null、undefined
    formatNumber(num) {
      // 处理 null、undefined 或空值
      if (num === null || num === undefined || num === "") {
        return "0";
      }
      // 如果是字符串，先转换为数字
      if (typeof num === "string") {
        num = parseInt(num, 10) || 0;
      }
      // 确保是数字类型
      if (typeof num !== "number" || isNaN(num)) {
        return "0";
      }
      // 处理 0
      if (num === 0) {
        return "0";
      }
      // 格式化数字（添加千位分隔符）
      return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    },
    goPersonal() {
      if (!this.ensureAuthorized()) {
        return;
      }
      uni.navigateTo({ url: "/pages/donation/donation" });
    },
    goTeam() {
      if (!this.ensureAuthorized()) {
        return;
      }
      uni.navigateTo({ url: "/pages/team/index" });
    },
    goMusic() {
      if (!this.ensureAuthorized()) {
        return;
      }
      console.warn('goMusic', this.todaySteps);     // 判断是否有个人今日步数
      console.warn('showNoStepsModal', this.showNoStepsModal);
      if (!this.todaySteps || this.todaySteps <= 0) {
        this.showNoStepsModal = true;
        return;
      }
      uni.navigateTo({ url: "/pages/music/playlist" });
    },
    goStore() {
      if (!this.ensureAuthorized()) {
        return;
      }
      // 判断是否有个人今日步数
      if (!this.todaySteps || this.todaySteps <= 0) {
        this.showNoStepsModal = true;
        return;
      }
      // 打开查看网点
      uni.navigateTo({ url: "/pages/store/checkin" });
    },
    goPeripheral() {
      uni.navigateTo({ url: "/pages/store/guide" });
    },
    goLottery() {
      if (!this.ensureAuthorized()) {
        return;
      }
      uni.navigateTo({ url: "/pages/lottery/index" });
    },
    /**
     * 从奖品弹窗跳转到抽奖页面
     */
    handleLotteryFromPrizeModal() {
      this.hidePrizeModal();
      this.goLottery();
    },
    /**
     * 计算总抽奖次数
     * @param {Array} lottryTimes 抽奖次数数组
     * @returns {Number} 总抽奖次数
     */
    calculateLotteryTimes(lottryTimes) {
      if (!Array.isArray(lottryTimes) || lottryTimes.length === 0) {
        return 0;
      }
      // 对所有对象的 times 字段求和
      return lottryTimes.reduce((total, item) => {
        const times = parseInt(item.times, 10) || 0;
        return total + times;
      }, 0);
    },
    /**
     * 刷新抽奖次数
     * 从服务器获取最新的抽奖次数
     * @returns {Promise<Array>} 返回抽奖次数数组，失败时返回空数组
     */
    /**
     * 将 login 接口返回的数据统一同步到界面状态
     * @param {Object} loginRes login接口返回
     */
    async applyLoginData(loginRes) {
      if (!loginRes) return;
      // 用户信息
      this.userInfo = {
        openid: loginRes.openid || "",
        nickname: loginRes.nickname || this.userInfo.nickname || "未设置昵称",
        avatarUrl: loginRes.avatarUrl || this.userInfo.avatarUrl || "",
        gender: loginRes.gender || 0,
        country: loginRes.country || "",
        province: loginRes.province || "",
        city: loginRes.city || "",
        language: loginRes.language || "",
      };
      // initData 同步
      if (loginRes.initData) {
        // 个人总步数
        if (loginRes.initData.personalTotalSteps !== undefined && loginRes.initData.personalTotalSteps !== null && loginRes.initData.personalTotalSteps !== "") {
          const personal = parseInt(loginRes.initData.personalTotalSteps, 10);
          if (!isNaN(personal)) this.personalTotalSteps = personal;
        }
        // 今日步数
        if (loginRes.initData.todaySteps !== undefined && loginRes.initData.todaySteps !== null && loginRes.initData.todaySteps !== "") {
          const today = parseInt(loginRes.initData.todaySteps, 10);
          if (!isNaN(today)) this.todaySteps = today;
        }
        // 当前公益总步数（initData）
        if (loginRes.initData.projectTotalStep !== undefined && loginRes.initData.projectTotalStep !== null && loginRes.initData.projectTotalStep !== "") {
          const project = parseInt(loginRes.initData.projectTotalStep, 10);
          if (!isNaN(project)) this.projectTotalStep = project;
        }
        // 当前爱心基金
        if (loginRes.initData.projectTotalBenefit !== undefined && loginRes.initData.projectTotalBenefit !== null && loginRes.initData.projectTotalBenefit !== "") {
          const benefit = parseFloat(loginRes.initData.projectTotalBenefit);
          this.currentFund = isNaN(benefit) ? this.currentFund : benefit;
        }
        // 当前周次
        if (loginRes.initData.currentWeek !== undefined && loginRes.initData.currentWeek !== null && loginRes.initData.currentWeek !== "") {
          const week = parseInt(loginRes.initData.currentWeek, 10);
          if (!isNaN(week) && (week === 1 || week === 2)) {
            this.currentWeek = week;
            this.selectedPrizeWeek = this.currentWeek || 1;
          }
        }
        // 奖品
        if (loginRes.initData.prizes && Array.isArray(loginRes.initData.prizes)) {
          this.prizes = this.formatPrizes(loginRes.initData.prizes);
        } else {
          this.prizes = [];
        }
        // 抽奖次数
        if (loginRes.initData.lottryTimes) {
          this.lottryTimesData = loginRes.initData.lottryTimes;
          this.lotteryTimes = this.calculateLotteryTimes(loginRes.initData.lottryTimes);
        } else {
          this.lottryTimesData = [];
          this.lotteryTimes = 0;
        }
        // 线下报名是否已满
        const fullFlag = loginRes.initData.fullOfflane;
        this.isOfflineFull = fullFlag === true || fullFlag === 'true' || fullFlag === 1 || fullFlag === '1';
      }
      // 顶层兼容的项目总步数
      if (loginRes.projectTotalStep !== undefined && loginRes.projectTotalStep !== null && loginRes.projectTotalStep !== "") {
        const steps = parseInt(loginRes.projectTotalStep, 10);
        if (!isNaN(steps)) this.projectTotalStep = steps;
      }
    },
    /**
     * 统一的 login 并同步界面数据
     * @returns {Object} loginRes
     */
    async fetchAndSync() {
      const loginRes = await login();
      console.warn('loginRes1', loginRes);
      await this.applyLoginData(loginRes);
      return loginRes;
    },
    async refreshLotteryTimes() {
      try {
        const loginRes = await this.fetchAndSync();
        return loginRes?.initData?.lottryTimes || [];
      } catch (error) {
        console.error("刷新抽奖次数失败:", error);
        // 失败时不更新，保持当前值
        return [];
      }
    },
    goPrize() {
      uni.navigateTo({ url: "/pages/prize/my" });
    },
    goOfflineRegistration() {
      if (!this.ensureAuthorized()) {
        return;
      }
      // 检查是否已过2025年11月21日
      const now = new Date();
      const endDate = new Date('2025-11-21T23:59:59');
      
      if (now > endDate) {
        // 已过截止日期，显示已结束弹窗
        this.showOfflineEndModal = true;
        return;
      }
      
      // 如果线下活动已满，弹出提示，不跳转
      if (this.isOfflineFull) {
        this.showOfflineFullModal = true;
        return;
      }
      // 否则跳转到线下活动预约页面
      uni.navigateTo({ url: "/pages/activity/reservation" });
    },
    closeOfflineFullModal() {
      this.showOfflineFullModal = false;
    },
    closeOfflineEndModal() {
      this.showOfflineEndModal = false;
    },
    showRulesModal() {
      this.showRules = true;
    },
    hideRulesModal() {
      this.showRules = false;
    },
    showPrizeModal() {
      if (!this.ensureAuthorized()) {
        return;
      }
      this.showPrize = true;
      // 打开弹窗时根据 currentWeek 设置默认周次，如果没有则默认为第一周
      this.selectedPrizeWeek = this.currentWeek || 1;
    },
    hidePrizeModal() {
      this.showPrize = false;
    },
    handlePrizeAction(prize) {
      if (prize.status === "claimed") {
        return;
      }
      switch (prize.status) {
        case "available":
          // 领取逻辑
          uni.showToast({
            title: "领取成功",
            icon: "success",
          });
          prize.status = "claimed";
          prize.actionText = "已领取";
          break;
        case "code":
          // 跳转到兑换奖品页面
          // 判断奖品类型：京东E卡或QQ音乐
          let prizeType = "jd";
          if (
            prize.name &&
            (prize.name.includes("QQ音乐") || prize.name.includes("qq音乐"))
          ) {
            prizeType = "qqmusic";
          }
          // 使用原始数据中的卡号和卡密
          const cardNumber = prize.originalData?.cardNumber || "";
          const cardSecret = prize.originalData?.cardSecret || "";
          uni.navigateTo({
            url: `/pages/prize/redeem?type=${prizeType}&code=${encodeURIComponent(
              cardNumber
            )}&secret=${encodeURIComponent(
              cardSecret
            )}&name=${encodeURIComponent(prize.name || "")}`,
          });
          // 关闭奖品弹窗
          this.hidePrizeModal();
          break;
        case "certificate":
          // 弹出证书查看弹窗
          this.hidePrizeModal();
          this.certificateDisplayUrl = this.certificateUrl;
          this.showCertificateModal = true;
          break;
        case "store":
          // 门店领取逻辑
          uni.showToast({
            title: "请前往指定门店领取",
            icon: "none",
          });
          break;
      }
    },
    startInitialAnimation() {
      // 确保小人在最左边开始
      this.hasInitialized = false;
      this.isAnimating = false;
      // 等待一帧，确保 DOM 更新，left 属性已经设置为 startOffset
      this.$nextTick(() => {
        // 再等待一小段时间，确保位置已经稳定
        setTimeout(() => {
          // 第一步：添加动画类，启用 CSS transition（left 2s linear）
          this.isAnimating = true;
          // 第二步：等待足够的时间（100ms），确保 transition 样式已经应用并生效
          setTimeout(() => {
            // 第三步：现在更新 left 为目标位置，CSS transition 会自动处理匀速移动
            this.hasInitialized = true;
            // 动画持续2秒，结束后移除动画类
            setTimeout(() => {
              this.isAnimating = false;
            }, 2000);
          }, 100); // 给足够时间让 transition 生效
        }, 50); // 给一点时间让初始位置稳定
      });
    },
    /**
     * 检测文案是否需要滚动
     */
    checkFundLabelScroll() {
      this.$nextTick(() => {
        setTimeout(() => {
          const query = uni.createSelectorQuery().in(this);
          query.select('.fund-label-container').boundingClientRect((containerRect) => {
            query.select('.fund-label-text-inner').boundingClientRect((textRect) => {
              if (containerRect && textRect && textRect.width > containerRect.width) {
                this.needScroll = true;
              }
            }).exec();
          }).exec();
        }, 100);
      });
    },
    /**
     * 检查用户认证状态
     */
    async checkUserAuth() {
      try {
        // 检查是否有有效的 OpenID（未过期）
        if (hasValidOpenID()) {
          // 已有有效的 OpenID，直接获取用户信息
          await this.fetchUserInfo();
        } else {
          // 没有有效的 OpenID，显示授权弹窗
          this.showAuthModal = true;
        }
      } catch (error) {
        console.error("检查用户认证状态失败:", error);
        this.showAuthModal = true;
      }
    },
    /**
     * 处理头像选择（新版本头像昵称填写能力）
     */
    async onChooseAvatar(e) {
      const { avatarUrl } = e.detail;
      console.log("选择头像:", avatarUrl);

      // 显示上传中提示
      uni.showLoading({
        title: "上传头像中...",
        mask: true,
      });

      try {
        // 1. 获取OSS上传凭证
        const ticket = await this.getOssTicket();
        console.log("获取OSS上传凭证成功:", ticket);

        // 2. 使用已获取的凭证上传头像到OSS获取长期链接
        const permanentUrl = await this.uploadImageToOss(
          avatarUrl,
          null,
          "static/higc",
          ticket
        );
        console.log("头像上传成功，长期链接:", permanentUrl);

        // 3. 保存长期链接到临时头像URL
        this.tempAvatarUrl = permanentUrl;

        uni.hideLoading();
        uni.showToast({
          title: "头像上传成功",
          icon: "success",
          duration: 2000,
        });
      } catch (error) {
        console.error("上传头像失败:", error);
        uni.hideLoading();

        // 上传失败时，仍然保存临时路径，允许用户继续操作
        this.tempAvatarUrl = avatarUrl;

        uni.showToast({
          title: error.message || "上传头像失败，请重试",
          icon: "none",
          duration: 2000,
        });
      }
    },
    /**
     * 处理昵称输入
     */
    onNicknameInput(e) {
      this.tempNickname = e.detail.value;
    },
    /**
     * 处理昵称失焦
     */
    onNicknameBlur(e) {
      this.tempNickname = e.detail.value;
    },
    /**
     * 处理手机号输入
     */
    onMobileInput(e) {
      this.tempMobile = e.detail.value;
    },
    /**
     * 处理手机号失焦
     */
    onMobileBlur(e) {
      this.tempMobile = e.detail.value;
    },
    /**
     * 处理微信授权获取手机号
     */
    async handleGetPhoneNumber(e) {
      console.log("获取手机号事件", e);

      // 检查是否取消授权
      if (e.detail.errMsg && e.detail.errMsg.includes("deny")) {
        uni.showToast({
          title: "已取消授权",
          icon: "none",
        });
        return;
      }

      // 新版本微信小程序直接返回 code，需要后端解密
      if (e.detail.code) {
        try {
          uni.showLoading({
            title: "获取中...",
          });

          // 调用后端接口解密手机号
          const phoneNumber = await this.decryptPhoneNumber(e.detail.code);

          if (phoneNumber) {
            // 将手机号填入表单
            this.tempMobile = phoneNumber;
            uni.showToast({
              title: "获取成功",
              icon: "success",
            });
          } else {
            throw new Error("获取手机号失败");
          }
        } catch (error) {
          console.error("获取手机号失败", error);
          // 检查是否是404错误（后端接口未实现）
          const errorMsg = error.message || "";
          const errorCode = error.code || error.statusCode || "";

          if (
            errorCode === 404 ||
            errorMsg.includes("404") ||
            errorMsg.includes("not found")
          ) {
            uni.showModal({
              title: "功能暂不可用",
              content:
                "微信授权获取手机号功能需要后端接口支持。请先手动输入手机号码，或联系后端开发人员实现 /jstc/v1/phone_decrypt 接口。",
              showCancel: false,
              confirmText: "知道了",
            });
          } else {
            uni.showToast({
              title: errorMsg || "获取手机号失败，请手动输入",
              icon: "none",
              duration: 3000,
            });
          }
        } finally {
          uni.hideLoading();
        }
      } else {
        // 旧版本兼容处理（包含 encryptedData 和 iv）
        if (e.detail.encryptedData && e.detail.iv) {
          try {
            uni.showLoading({
              title: "获取中...",
            });

            // 调用后端接口解密手机号（旧版本）
            const phoneNumber = await this.decryptPhoneNumberOld(
              e.detail.encryptedData,
              e.detail.iv
            );

            if (phoneNumber) {
              this.tempMobile = phoneNumber;
              uni.showToast({
                title: "获取成功",
                icon: "success",
              });
            } else {
              throw new Error("获取手机号失败");
            }
          } catch (error) {
            console.error("获取手机号失败", error);
            // 检查是否是404错误（后端接口未实现）
            const errorMsg = error.message || "";
            const errorCode = error.code || error.statusCode || "";

            if (
              errorCode === 404 ||
              errorMsg.includes("404") ||
              errorMsg.includes("not found")
            ) {
              uni.showModal({
                title: "功能暂不可用",
                content:
                  "微信授权获取手机号功能需要后端接口支持。请先手动输入手机号码，或联系后端开发人员实现 /jstc/v1/phone_decrypt 接口。",
                showCancel: false,
                confirmText: "知道了",
              });
            } else {
              uni.showToast({
                title: errorMsg || "获取手机号失败，请手动输入",
                icon: "none",
                duration: 3000,
              });
            }
          } finally {
            uni.hideLoading();
          }
        } else {
          uni.showToast({
            title: "获取手机号失败，请手动输入",
            icon: "none",
          });
        }
      }
    },
    /**
     * 解密手机号（新版本，使用 code）
     */
    async decryptPhoneNumber(code) {
      try {
        // 尝试获取 openid（可选参数，根据微信文档）
        const loginData = userStore.getLoginData();
        const openid = loginData && loginData.openid ? loginData.openid : null;

        // 构建请求参数
        const requestData = { code: code || "" };
        if (openid) {
          requestData.openid = openid;
        }

        // 不显示 loading（因为已经在 handleGetPhoneNumber 中显示了）
        const response = await decryptPhoneFromAPI(requestData, {
          showLoading: false,
        });

        // 返回手机号（后端返回格式：{phone: "****"} 或 {phoneNumber: "****"}）
        return response.phone || response.phoneNumber;
      } catch (error) {
        console.error("解密手机号失败", error);
        throw error;
      }
    },
    /**
     * 解密手机号（旧版本，使用 encryptedData 和 iv）
     */
    async decryptPhoneNumberOld(encryptedData, iv) {
      try {
        // 不显示 loading（因为已经在 handleGetPhoneNumber 中显示了）
        const response = await decryptPhoneFromAPI(
          {
            encryptedData,
            iv,
          },
          {
            showLoading: false,
          }
        );

        // 返回手机号（后端返回格式：{phone: "****"} 或 {phoneNumber: "****"}）
        return response.phone || response.phoneNumber;
      } catch (error) {
        console.error("解密手机号失败", error);
        throw error;
      }
    },
    // 兼容非微信小程序环境
    getWechatPhone() {
      uni.showToast({
        title: "请在微信小程序中使用",
        icon: "none",
      });
    },
    /**
     * 校验手机号格式
     * @param {string} mobile 手机号
     * @returns {boolean} 是否为有效手机号
     */
    validateMobile(mobile) {
      if (!mobile || mobile.trim() === "") {
        return false;
      }
      // 手机号格式校验：1开头的11位数字
      return /^1[3-9]\d{9}$/.test(mobile.trim());
    },
    /**
     * 处理用户授权确认
     */
    async handleAuthConfirm() {
      try {
        // 进入授权确认后，优先清空本地缓存，避免使用旧数据
        try {
          uni.clearStorageSync()
        } catch (clearErr) {
          console.warn('清空本地缓存失败:', clearErr)
        }
        // 新版本：检查是否已填写头像、昵称和手机号
        if (this.supportNewUserInfo) {
          if (!this.tempAvatarUrl || !this.tempNickname || !this.tempMobile) {
            uni.showToast({
              title: "请先选择头像、输入昵称和手机号",
              icon: "none",
              duration: 2000,
            });
            return;
          }
          // 校验手机号格式
          if (!this.validateMobile(this.tempMobile)) {
            uni.showToast({
              title: "请输入正确的手机号码",
              icon: "none",
              duration: 2000,
            });
            return;
          }
        }

        this.showAuthModal = false;

        // 1. 获取用户信息
        let userProfileData = null;

        // #ifdef MP-WEIXIN
        if (this.supportNewUserInfo) {
          // 新版本：使用用户填写的信息
          userProfileData = {
            avatarUrl: this.tempAvatarUrl,
            nickName: this.tempNickname,
            // 新版本填写能力无法获取这些信息，使用默认值
            gender: 0,
            country: "",
            province: "",
            city: "",
            language: "",
            // 包含手机号
            phone: this.tempMobile,
          };
          // 清空临时数据
          this.tempAvatarUrl = "";
          this.tempNickname = "";
          this.tempMobile = "";
        } else {
          // 旧版本：使用 getUserProfile（仅低版本基础库可用）
          try {
            const profileRes = await new Promise((resolve, reject) => {
              uni.getUserProfile({
                desc: "用于完善用户资料",
                success: resolve,
                fail: reject,
              });
            });

            if (profileRes && profileRes.userInfo) {
              userProfileData = profileRes.userInfo;
            }
          } catch (profileError) {
            console.warn("获取用户信息失败（用户可能拒绝授权）:", profileError);
            // 用户拒绝授权不影响登录流程，继续执行
          }
        }
        // #endif

        // 2. 获取微信登录code
        const loginRes = await new Promise((resolve, reject) => {
          // #ifdef MP-WEIXIN
          uni.login({
            provider: "weixin",
            success: resolve,
            fail: reject,
          });
          // #endif
          // #ifndef MP-WEIXIN
          reject(new Error("非微信小程序环境"));
          // #endif
        });

        if (!loginRes.code) {
          throw new Error("获取微信登录code失败");
        }

        // 3. 调用 code2Session 获取 openid（不重试）
        // code2Session 会自动保存 openid 到本地存储并设置6小时缓存
        try {
          const sessionRes = await code2Session({ code: loginRes.code });

          if (!sessionRes.openid) {
            throw new Error("获取用户标识失败");
          }

          // 4. 如果有用户信息，更新到后端
          if (userProfileData) {
            await this.updateUserProfileToServer(userProfileData);
          }

          // 5. 获取完整的用户信息（会自动处理teamId逻辑）
          await this.fetchUserInfo();
        } catch (code2SessionError) {
          // code2Session 失败，不再尝试
          console.error("code2Session 失败:", code2SessionError);
          throw code2SessionError;
        }
      } catch (error) {
        console.error("用户授权失败:", error);
        uni.showToast({
          title: error.message || "授权失败，请重试",
          icon: "none",
          duration: 2000,
        });
        // 授权失败，重新显示授权弹窗
        setTimeout(() => {
          this.showAuthModal = true;
        }, 2000);
      }
    },
    /**
     * 更新用户信息到服务器
     * @param {Object} userInfo 用户信息对象
     */
    async updateUserProfileToServer(userInfo) {
      try {
        const updateData = {
          nickname: userInfo.nickName || userInfo.nickname || "",
          avatarUrl: userInfo.avatarUrl || "",
          gender: userInfo.gender || 0,
          country: userInfo.country || "",
          province: userInfo.province || "",
          city: userInfo.city || "",
          language: userInfo.language || "",
        };
        // 如果有手机号，也一起更新
        // 注意：phone 字段在接口文档中暂时没有，但接口已支持，需要备注
        if (userInfo.phone) {
          updateData.phone = userInfo.phone;
        }
        await updateUserProfile(updateData);
      } catch (updateError) {
        // 检查是否是授权错误（不重试）
        if (
          updateError &&
          updateError.code === 1002 &&
          updateError.message === "ERROR_CODE_UNAUTHORIZED"
        ) {
          console.warn("更新用户信息时授权错误，不重试");
          return;
        }
        // 其他错误只记录，不阻断流程
        console.warn("更新用户信息到服务器失败:", updateError);
      }
    },
    /**
     * 获取用户信息
     * 调用 login 接口获取最新数据，数据会自动保存到全局状态（userStore）
     * 其他页面可以通过 userStore.getLoginData() 获取静态数据，无需重复请求
     */
    async fetchUserInfo() {
      try {
        // 统一调用：登录并同步界面数据
        const loginRes = await this.fetchAndSync();
        // 如果有shareTeamId，处理团队加入逻辑（在已有OpenID的情况下）
        // 传入 loginRes 避免重复调用 login 接口
        if (this.shareTeamId && hasValidOpenID()) {
          await this.handleTeamJoin(loginRes);
        }
        // 检查是否有手机号，如果没有则显示手机号弹窗
        this.checkPhoneAndShowModal(loginRes);
      } catch (error) {
        console.error("获取用户信息失败:", error);
        // 检查是否是授权错误（不重试）
        if (
          error &&
          error.code === 1002 &&
          error.message === "ERROR_CODE_UNAUTHORIZED"
        ) {
          console.warn("授权错误，不重试");
          uni.removeStorageSync("x-wxuser");
          this.showAuthModal = true;
          return;
        }
        // 如果获取失败，可能是token过期，重新显示授权弹窗
        if (error.statusCode === 401 || error.statusCode === 403) {
          uni.removeStorageSync("x-wxuser");
          this.showAuthModal = true;
        }
      }
    },
    /**
     * 检查是否有手机号，如果没有则显示手机号弹窗
     * 条件：用户有openid、头像、昵称，唯独缺少手机号时才显示
     * @param {Object} loginRes login接口返回的数据
     */
    checkPhoneAndShowModal(loginRes) {
      if (!loginRes) {
        return;
      }
      
      // 检查是否有openid
      const hasOpenid = loginRes.openid && loginRes.openid.trim() !== "";
      // 检查是否有头像
      const hasAvatar = loginRes.avatarUrl && loginRes.avatarUrl.trim() !== "";
      // 检查是否有昵称
      const hasNickname = loginRes.nickname && loginRes.nickname.trim() !== "";
      // 检查是否有手机号（可能在顶层，也可能在 userInfo 中）
      const phone = loginRes.phone || loginRes.userInfo?.phone || "";
      const hasPhone = phone && phone.trim() !== "" && phone !== "0";
      
      // 如果用户有openid、头像、昵称，唯独缺少手机号，则显示手机号弹窗
      if (hasOpenid && hasAvatar && hasNickname && !hasPhone) {
        this.showPhoneModal = true;
        this.phoneModalMobile = "";
      }
    },
    /**
     * 处理手机号弹窗输入
     */
    onPhoneModalInput(e) {
      this.phoneModalMobile = e.detail.value;
    },
    /**
     * 处理手机号弹窗失焦
     */
    onPhoneModalBlur(e) {
      this.phoneModalMobile = e.detail.value;
    },
    /**
     * 处理手机号弹窗微信授权获取手机号
     */
    async handlePhoneModalGetPhoneNumber(e) {
      console.log("获取手机号事件", e);

      // 检查是否取消授权
      if (e.detail.errMsg && e.detail.errMsg.includes("deny")) {
        uni.showToast({
          title: "已取消授权",
          icon: "none",
        });
        return;
      }

      // 新版本微信小程序直接返回 code，需要后端解密
      if (e.detail.code) {
        try {
          uni.showLoading({
            title: "获取中...",
          });

          // 调用后端接口解密手机号
          const phoneNumber = await this.decryptPhoneNumber(e.detail.code);

          if (phoneNumber) {
            // 将手机号填入表单
            this.phoneModalMobile = phoneNumber;
            uni.showToast({
              title: "获取成功",
              icon: "success",
            });
          } else {
            throw new Error("获取手机号失败");
          }
        } catch (error) {
          console.error("获取手机号失败", error);
          // 检查是否是404错误（后端接口未实现）
          const errorMsg = error.message || "";
          const errorCode = error.code || error.statusCode || "";

          if (
            errorCode === 404 ||
            errorMsg.includes("404") ||
            errorMsg.includes("not found")
          ) {
            uni.showModal({
              title: "功能暂不可用",
              content:
                "微信授权获取手机号功能需要后端接口支持。请先手动输入手机号码，或联系后端开发人员实现 /jstc/v1/phone_decrypt 接口。",
              showCancel: false,
              confirmText: "知道了",
            });
          } else {
            uni.showToast({
              title: errorMsg || "获取手机号失败，请手动输入",
              icon: "none",
              duration: 3000,
            });
          }
        } finally {
          uni.hideLoading();
        }
      } else {
        // 旧版本兼容处理（包含 encryptedData 和 iv）
        if (e.detail.encryptedData && e.detail.iv) {
          try {
            uni.showLoading({
              title: "获取中...",
            });

            // 调用后端接口解密手机号（旧版本）
            const phoneNumber = await this.decryptPhoneNumberOld(
              e.detail.encryptedData,
              e.detail.iv
            );

            if (phoneNumber) {
              this.phoneModalMobile = phoneNumber;
              uni.showToast({
                title: "获取成功",
                icon: "success",
              });
            } else {
              throw new Error("获取手机号失败");
            }
          } catch (error) {
            console.error("获取手机号失败", error);
            // 检查是否是404错误（后端接口未实现）
            const errorMsg = error.message || "";
            const errorCode = error.code || error.statusCode || "";

            if (
              errorCode === 404 ||
              errorMsg.includes("404") ||
              errorMsg.includes("not found")
            ) {
              uni.showModal({
                title: "功能暂不可用",
                content:
                  "微信授权获取手机号功能需要后端接口支持。请先手动输入手机号码，或联系后端开发人员实现 /jstc/v1/phone_decrypt 接口。",
                showCancel: false,
                confirmText: "知道了",
              });
            } else {
              uni.showToast({
                title: errorMsg || "获取手机号失败，请手动输入",
                icon: "none",
                duration: 3000,
              });
            }
          } finally {
            uni.hideLoading();
          }
        } else {
          uni.showToast({
            title: "获取手机号失败，请手动输入",
            icon: "none",
          });
        }
      }
    },
    /**
     * 处理手机号弹窗取消
     */
    handlePhoneCancel() {
      uni.showModal({
        title: "提示",
        content: "手机号用于完善用户信息，是否继续？",
        confirmText: "重新填写",
        cancelText: "暂不填写",
        success: (res) => {
          if (res.confirm) {
            this.showPhoneModal = true;
          } else {
            this.showPhoneModal = false;
          }
        },
      });
    },
    /**
     * 处理手机号弹窗确认
     * 校验手机号并调用 update_user_profile 接口更新
     */
    async handlePhoneConfirm() {
      // 校验手机号是否填写
      if (!this.phoneModalMobile || this.phoneModalMobile.trim() === "") {
        uni.showToast({
          title: "请输入手机号码",
          icon: "none",
          duration: 2000,
        });
        return;
      }

      // 校验手机号格式
      if (!this.validateMobile(this.phoneModalMobile)) {
        uni.showToast({
          title: "请输入正确的手机号码",
          icon: "none",
          duration: 2000,
        });
        return;
      }

      try {
        uni.showLoading({
          title: "更新中...",
        });

        // 调用 update_user_profile 接口更新手机号
        // 注意：phone 字段在接口文档中暂时没有，但接口已支持，需要备注
        // 当只更新手机号时，也需要传递其他字段，其余数据都用空字符串
        // 获取当前用户的 openid（从 userInfo 或 userStore）
        const currentOpenid = this.userInfo.openid || (userStore.getLoginData()?.openid) || "";
        const updateData = {
          openid: currentOpenid,
          nickname: "",
          avatarUrl: "",
          gender: 0,
          country: "",
          province: "",
          city: "",
          language: "",
          phone: this.phoneModalMobile.trim(),
        };
        await updateUserProfile(updateData);

        uni.hideLoading();
        uni.showToast({
          title: "更新成功",
          icon: "success",
        });

        // 关闭弹窗
        this.showPhoneModal = false;
        this.phoneModalMobile = "";

        // 刷新用户信息（重新调用 login 接口获取最新数据）
        await this.fetchUserInfo();
      } catch (error) {
        console.error("更新手机号失败", error);
        uni.hideLoading();
        const errorMsg =
          error.message || error.msg || "更新手机号失败，请稍后重试";
        uni.showToast({
          title: errorMsg,
          icon: "none",
          duration: 3000,
        });
      }
    },
    /**
     * 处理团队加入逻辑
     * 根据用户的组队情况显示不同的弹窗
     * @param {Object} loginRes - 可选的登录响应数据，如果传入则避免重复调用 login 接口
     */
    async handleTeamJoin(loginRes = null) {
      // 如果没有 shareTeamId 则 return
      if (!this.shareTeamId) {
        return;
      }
      if (!hasValidOpenID()) {
        this.showAuthModal = true;
        return;
      }

      try {
        // 先用 login 获取最新的团队信息
        if (!loginRes) {
          loginRes = await login();
          console.warn('loginRes2', loginRes);
        }

        if (!loginRes) {
          console.warn("获取登录数据失败");
          return;
        }

        const teamInfo = loginRes.teamInfo;

        if (!teamInfo) {
          // 没有团队信息，询问是否加入该团队
          this.showTeamJoinConfirmModal = true;
          return;
        }

        // 获取当前用户的 openid 和成员列表
        const currentUserOpenid = loginRes.openid;
        const members = teamInfo.members || [];

        // 检查 members 数组中是否只有自己
        const isOnlySelf =
          Array.isArray(members) &&
          members.length === 1 &&
          members[0].openid === currentUserOpenid;

        if (!isOnlySelf) {
          // 如果 members 数组中不只有自己（有其他成员），显示已加入弹窗
          this.showTeamAlreadyJoinedModal = true;
          return;
        }

        // 如果只有自己，且 shareTeamId != teamInfo.teamId，显示加入确认弹窗
        if (this.shareTeamId !== teamInfo.teamId) {
          this.showTeamJoinConfirmModal = true;
        }
      } catch (error) {
        console.error("处理团队加入逻辑失败:", error);
        uni.showToast({
          title: "加载失败",
          icon: "none",
        });
      }
    },
    /**
     * 确认加入团队
     */
    async confirmJoinTeam() {
      if (!this.shareTeamId) {
        return;
      }

      try {
        this.showTeamJoinModal = false;

        // 调用加入团队接口
        await joinTeam({ teamId: this.shareTeamId });

        // 加入成功，显示成功弹窗
        this.showTeamJoinSuccessModal = true;
      } catch (error) {
        console.error("加入团队失败:", error);
        uni.showToast({
          title: error.message || "加入失败，请重试",
          icon: "none",
        });
      }
    },
    /**
     * 取消加入团队
     */
    cancelJoinTeam() {
      this.showTeamJoinModal = false;
      this.showTeamWelcomeModal = true;
    },
    /**
     * 关闭期待加入弹窗
     */
    closeWelcomeModal() {
      this.showTeamWelcomeModal = false;
      this.shareTeamId = null; // 清除teamId，避免重复处理
    },
    /**
     * 关闭快去和别人组队吧弹窗
     */
    closeGoInviteModal() {
      this.showTeamGoInviteModal = false;
      this.shareTeamId = null; // 清除teamId，避免重复处理
    },
    /**
     * 关闭组队成功弹窗
     */
    closeTeamJoinSuccessModal() {
      this.showTeamJoinSuccessModal = false;
      this.shareTeamId = null; // 清除teamId，避免重复处理
    },
    /**
     * 跳转到团队页面
     */
    goToTeamIndex() {
      this.showTeamJoinSuccessModal = false;
      uni.navigateTo({ url: "/pages/team/index" });
    },
    /**
     * 关闭你已经加入过团队弹窗
     */
    closeTeamAlreadyJoinedModal() {
      this.showTeamAlreadyJoinedModal = false;
      this.shareTeamId = null; // 清除teamId，避免重复处理
    },
    /**
     * 从你已经加入过团队弹窗跳转到团队页面
     */
    goToTeamIndexFromAlreadyJoined() {
      this.showTeamAlreadyJoinedModal = false;
      uni.navigateTo({ url: "/pages/team/index" });
    },
    /**
     * 确认加入该团队
     */
    async confirmJoinTeamFromConfirm() {
      if (!this.shareTeamId) {
        return;
      }

      try {
        this.showTeamJoinConfirmModal = false;

        // 调用加入团队接口
        await joinTeam({ teamId: this.shareTeamId });

        // 加入成功，显示成功弹窗
        this.showTeamJoinSuccessModal = true;
      } catch (error) {
        console.error("加入团队失败:", error);
        uni.showToast({
          title: error.message || "加入失败，请重试",
          icon: "none",
        });
      }
    },
    /**
     * 取消加入该团队
     */
    cancelJoinTeamFromConfirm() {
      this.showTeamJoinConfirmModal = false;
    },
    /**
     * 获取弹窗文字颜色（通用函数）
     * @param {string} buttonText - 按钮文本
     * @returns {string} 返回颜色值
     */
    getModalTextColor(buttonText) {
      // 当按钮是"立即捐步"时，使用蓝色，否则使用红色
      if (buttonText === "立即捐步") {
        return "#1b6ec1";
      }
      return "#f03c32";
    },
    /**
     * 跳转到团队页面（已有队伍的情况）
     */
    goToTeamPage() {
      this.showTeamDifferentModal = false;
      uni.navigateTo({
        url: "/pages/team/index",
      });
    },
    /**
     * 关闭已在队伍弹窗
     */
    closeAlreadyInModal() {
      this.showTeamAlreadyInModal = false;
      this.shareTeamId = null; // 清除teamId，避免重复处理
    },
    /**
     * 从"已经在此队伍中"弹窗跳转到团队页面
     */
    goToTeamPageFromAlreadyIn() {
      this.showTeamAlreadyInModal = false;
      this.shareTeamId = null; // 清除teamId，避免重复处理
      uni.navigateTo({
        url: "/pages/team/index",
      });
    },
    /**
     * 处理授权取消
     */
    handleAuthCancel() {
      uni.showModal({
        title: "提示",
        content: "授权后才能使用完整功能，是否继续？",
        confirmText: "重新授权",
        cancelText: "暂不使用",
        success: (res) => {
          if (res.confirm) {
            this.showAuthModal = true;
          } else {
            this.showAuthModal = false;
          }
        },
      });
    },
    /**
     * 处理头像点击（可以重新获取/更新头像）
     */
    handleAvatarClick() {
      // 显示授权弹窗，允许用户重新选择头像和昵称
      // watch 会自动预填充现有信息
      this.showAuthModal = true;
    },
    /**
     * 处理昵称点击（可以重新获取/更新昵称）
     */
    handleNicknameClick() {
      // 显示授权弹窗，允许用户重新选择头像和昵称
      // watch 会自动预填充现有信息
      this.showAuthModal = true;
    },
    /**
     * 关闭需要先去捐步弹窗
     */
    closeNoStepsModal() {
      this.showNoStepsModal = false;
    },
    /**
     * 跳转到捐步页面
     */
    goToDonation() {
      this.showNoStepsModal = false;
      uni.navigateTo({ url: "/pages/donation/donation" });
    },
    /**
     * 证书相关：关闭弹窗
     */
    closeCertificateModal() {
      this.showCertificateModal = false;
    },
    /**
     * 证书图片加载失败，回退到本地占位图
     */
    onCertificateImageError() {
      this.certificateDisplayUrl = "/static/box.png";
    },
    /**
     * 保存证书到相册
     */
    async saveCertificate() {
      const url =
        this.certificateDisplayUrl || this.certificateUrl || "/static/box.png";
      const timeoutMs = 8000;
      try {
        uni.showLoading({ title: "保存中...", mask: true });
        let tempPath = "";
        // #ifndef H5
        // 非H5：优先下载，超时/失败则用 getImageInfo，再失败回退到本地占位图
        try {
          tempPath = await this._downloadWithTimeout(url, timeoutMs);
        } catch (_) {
          try {
            tempPath = await this._getImageInfoWithTimeout(url, timeoutMs);
          } catch (_) {
            tempPath = await this._getImageInfoWithTimeout(
              "/static/box.png",
              3000
            );
          }
        }
        // 保存到相册
        await new Promise((resolve, reject) => {
          uni.saveImageToPhotosAlbum({
            filePath: tempPath,
            success: resolve,
            fail: reject,
          });
        });
        // #endif
        // #ifdef H5
        // H5：触发浏览器下载
        const link = document.createElement("a");
        link.href = url;
        link.download = "certificate.png";
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        // #endif
        uni.showToast({ title: "已保存", icon: "success" });
      } catch (err) {
        if (err && (err.errMsg || "").includes("auth deny")) {
          uni.showModal({
            title: "提示",
            content: "需要相册权限以保存图片，是否前往设置开启？",
            success: (res) => {
              if (res.confirm) uni.openSetting({});
            },
          });
        } else {
          uni.showToast({ title: "保存失败，请重试", icon: "none" });
        }
      } finally {
        uni.hideLoading();
      }
    },
    /**
     * 工具：带超时的下载
     */
    _downloadWithTimeout(url, timeoutMs = 8000) {
      return new Promise((resolve, reject) => {
        let done = false;
        const timer = setTimeout(() => {
          if (done) return;
          done = true;
          reject(new Error("download timeout"));
        }, timeoutMs);
        uni.downloadFile({
          url,
          success: (res) => {
            if (done) return;
            done = true;
            clearTimeout(timer);
            if (res.statusCode === 200) resolve(res.tempFilePath);
            else reject(new Error("下载失败"));
          },
          fail: (err) => {
            if (done) return;
            done = true;
            clearTimeout(timer);
            reject(err);
          },
        });
      });
    },
    /**
     * 工具：带超时的 getImageInfo
     */
    _getImageInfoWithTimeout(src, timeoutMs = 8000) {
      return new Promise((resolve, reject) => {
        let done = false;
        const timer = setTimeout(() => {
          if (done) return;
          done = true;
          reject(new Error("getImageInfo timeout"));
        }, timeoutMs);
        uni.getImageInfo({
          src,
          success: (info) => {
            if (done) return;
            done = true;
            clearTimeout(timer);
            resolve(info.path || info.tempFilePath);
          },
          fail: (err) => {
            if (done) return;
            done = true;
            clearTimeout(timer);
            reject(err);
          },
        });
      });
    },
    /**
     * 获取弹窗吉祥物图片（通用函数）
     * @param {string} buttonText - 按钮文本
     * @returns {string} 返回图片路径
     */
    getModalMascotImage(buttonText) {
      // 当按钮是"立即捐步"时，使用资源 4.png，否则使用资源 33-8.png
      if (buttonText === "立即捐步") {
        return "/static/资源 4.png";
      }
      return "/static/资源 33-8.png";
    },
    /**
     * 获取OSS上传凭证
     * 调用后端接口获取OSS上传所需的临时凭证（policy、signature、accessid、host等）
     * @returns {Promise<Object>} 返回包含OSS上传凭证的对象
     * @returns {string} returns.policy - OSS策略文档
     * @returns {string} returns.signature - OSS签名
     * @returns {string} returns.accessid - OSS访问密钥ID
     * @returns {string} returns.host - OSS服务地址
     * @returns {number} returns.expire - 凭证过期时间戳
     */
    async getOssTicket() {
      return new Promise((resolve, reject) => {
        uni.request({
          url: "https://cgc.hylinkad.com/hyper/get-ticket",
          method: "GET",
          timeout: 10000, // 10秒超时
          success: (res) => {
            console.log("getOssTicket 响应:", res);

            // 检查HTTP状态码
            if (res.statusCode >= 200 && res.statusCode < 300) {
              try {
                // 处理响应数据
                if (res.data === null || res.data === undefined) {
                  reject(new Error("获取上传凭证失败：响应数据为空"));
                  return;
                }

                // 解析响应数据（可能是字符串或对象）
                let responseData;
                if (typeof res.data === "string") {
                  if (res.data.trim() === "") {
                    reject(new Error("获取上传凭证失败：响应数据为空字符串"));
                    return;
                  }
                  try {
                    responseData = JSON.parse(res.data);
                  } catch (parseError) {
                    reject(
                      new Error(
                        `解析 JSON 失败: ${parseError.message}, 原始数据: ${res.data}`
                      )
                    );
                    return;
                  }
                } else if (typeof res.data === "object") {
                  responseData = res.data;
                } else {
                  reject(
                    new Error(
                      `获取上传凭证失败：响应数据类型不正确，类型: ${typeof res.data}`
                    )
                  );
                  return;
                }

                console.log("解析后的响应数据:", responseData);

                // 检查数据结构：支持两种格式
                // 格式1: { data: { policy, signature, ... } }
                // 格式2: { policy, signature, ... } (直接返回)
                let ticketData = null;
                if (responseData && typeof responseData === "object") {
                  if (
                    responseData.data &&
                    typeof responseData.data === "object"
                  ) {
                    // 格式1: 数据嵌套在 data 字段中
                    ticketData = responseData.data;
                  } else if (
                    responseData.policy &&
                    responseData.signature &&
                    responseData.accessid &&
                    responseData.host
                  ) {
                    // 格式2: 数据直接平铺在响应对象中
                    ticketData = responseData;
                  }
                }

                if (ticketData) {
                  // 验证必需的字段
                  if (
                    !ticketData.policy ||
                    !ticketData.signature ||
                    !ticketData.accessid ||
                    !ticketData.host
                  ) {
                    reject(new Error("获取上传凭证失败：凭证信息不完整"));
                    return;
                  }

                  // 返回凭证信息
                  resolve({
                    policy: ticketData.policy,
                    signature: ticketData.signature,
                    accessid: ticketData.accessid,
                    host: ticketData.host,
                    bucket: ticketData.bucket || null, // 如果后端返回了 bucket，使用它
                    expire: ticketData.expire || 0,
                  });
                } else {
                  reject(
                    new Error(
                      "获取上传凭证失败：响应数据格式不正确，缺少必需的字段"
                    )
                  );
                }
              } catch (error) {
                console.error("处理响应数据失败:", error);
                reject(new Error(`处理响应数据失败: ${error.message}`));
              }
            } else {
              reject(
                new Error(`获取上传凭证失败，HTTP状态码: ${res.statusCode}`)
              );
            }
          },
          fail: (err) => {
            console.error("请求失败:", err);
            reject(new Error(err.errMsg || "获取上传凭证失败"));
          },
        });
      });
    },
    /**
     * 上传图片到OSS获取永久链接
     * 使用OSS PostObject方式上传图片，返回可访问的永久URL
     * @param {string} filePath - 本地图片文件路径（从uni.chooseImage获取的tempFilePaths）
     * @param {string} fileName - 可选，指定上传后的文件名，如果不提供则自动生成
     * @param {string} folder - 可选，OSS中的文件夹路径，默认为'images'
     * @param {Object} ticket - 可选，已获取的OSS上传凭证，如果不提供则自动获取
     * @returns {Promise<string>} 返回上传成功后图片的永久访问URL
     * @example
     * // 选择图片并上传
     * uni.chooseImage({
     *   count: 1,
     *   success: async (res) => {
     *     const imageUrl = await this.uploadImageToOss(res.tempFilePaths[0], 'my-image.jpg', 'avatars')
     *     console.log('上传成功，图片URL:', imageUrl)
     *   }
     * })
     */
    async uploadImageToOss(
      filePath,
      fileName = null,
      folder = "images",
      ticket = null
    ) {
      try {
        // 1. 获取OSS上传凭证（如果未提供）
        if (!ticket) {
          ticket = await this.getOssTicket();
        }

        // 验证凭证信息
        if (
          !ticket ||
          !ticket.policy ||
          !ticket.signature ||
          !ticket.accessid ||
          !ticket.host
        ) {
          throw new Error("获取上传凭证失败：凭证信息不完整");
        }

        // 1.5. 处理 filePath：如果是网络路径（http://tmp/ 或 http:// 开头），需要先下载到本地
        let localFilePath = filePath;
        if (filePath.startsWith("http://") || filePath.startsWith("https://")) {
          console.log("检测到网络路径，需要先下载到本地:", filePath);
          // 下载文件到本地临时路径
          const downloadResult = await new Promise((resolve, reject) => {
            uni.downloadFile({
              url: filePath,
              success: (res) => {
                if (res.statusCode === 200) {
                  console.log("文件下载成功，本地路径:", res.tempFilePath);
                  resolve(res.tempFilePath);
                } else {
                  reject(new Error(`下载文件失败，状态码: ${res.statusCode}`));
                }
              },
              fail: (err) => {
                console.error("下载文件失败:", err);
                reject(new Error(err.errMsg || "下载文件失败"));
              },
            });
          });
          localFilePath = downloadResult;
        }

        // 2. 生成文件名（如果未提供）
        if (!fileName) {
          const timestamp = Date.now();
          const random = Math.random().toString(36).substring(2, 8);
          // 从文件路径获取扩展名，默认为.jpg
          const ext =
            localFilePath.substring(localFilePath.lastIndexOf(".")) || ".jpg";
          fileName = `${timestamp}_${random}${ext}`;
        }

        // 3. 构建OSS中的文件路径（key）
        // OSS的key格式：folder/filename，例如：images/1234567890_abc123.jpg
        const ossKey = folder ? `${folder}/${fileName}` : fileName;

        // 4. 构建上传URL（OSS PostObject endpoint）
        // OSS PostObject endpoint 格式：https://{bucket-name}.oss-cn-{region}.aliyuncs.com
        let uploadUrl = ticket.host;
        // 确保URL包含协议
        if (
          !uploadUrl.startsWith("http://") &&
          !uploadUrl.startsWith("https://")
        ) {
          uploadUrl = `https://${uploadUrl}`;
        }
        // 检查 host 是否已经包含 bucket 名称（格式：bucket.oss-cn-xxx.aliyuncs.com）
        // 如果 host 是 oss-cn-xxx.aliyuncs.com（没有 bucket），需要添加 bucket
        // 优先级：1. ticket.bucket（后端返回） 2. 从 host 解析 3. 从 IMAGE_BASE_URL 配置提取 4. 默认值 'hykoc'

        // 先尝试从 host 解析 bucket 和区域
        // 格式1: bucket.oss-cn-xxx.aliyuncs.com
        // 格式2: oss-cn-xxx.aliyuncs.com（没有 bucket）
        const hostMatchWithBucket = uploadUrl.match(
          /https?:\/\/([^.]+)\.(oss-cn-[^.]+\.aliyuncs\.com)/
        );
        const hostMatchWithoutBucket = uploadUrl.match(
          /https?:\/\/(oss-cn-[^.]+\.aliyuncs\.com)/
        );
        const parsedBucket = hostMatchWithBucket
          ? hostMatchWithBucket[1]
          : null;
        const parsedRegion = hostMatchWithBucket
          ? hostMatchWithBucket[2]
          : hostMatchWithoutBucket
          ? hostMatchWithoutBucket[1]
          : null;

        // 从 IMAGE_BASE_URL 配置中提取 bucket 和区域（如果配置存在）
        // IMAGE_BASE_URL 格式：https://{bucket}.oss-cn-{region}.aliyuncs.com/...
        let configBucket = null;
        let configRegion = null;
        if (IMAGE_BASE_URL) {
          const configMatch = IMAGE_BASE_URL.match(
            /https?:\/\/([^.]+)\.(oss-cn-[^.]+\.aliyuncs\.com)/
          );
          if (configMatch) {
            configBucket = configMatch[1];
            configRegion = configMatch[2]; // 例如：oss-cn-beijing.aliyuncs.com
          }
        }

        // 确定使用的 bucket（优先级：ticket.bucket > host解析 > 配置提取 > 默认值）
        const bucketName =
          ticket.bucket || parsedBucket || configBucket || "hykoc";

        // 确定使用的区域（优先级：配置中的区域 > host中的区域）
        // 如果配置中有区域信息，优先使用配置中的区域（因为配置是已知正确的）
        const correctRegion =
          configRegion || parsedRegion || "oss-cn-beijing.aliyuncs.com";

        // 如果 host 不包含 bucket，添加 bucket 并使用正确的区域
        if (!parsedBucket) {
          // 从 oss-cn-xxx.aliyuncs.com 转换为 {bucket}.oss-cn-{correct-region}.aliyuncs.com
          uploadUrl = uploadUrl.replace(
            /https?:\/\/(oss-cn-[^.]+\.aliyuncs\.com)/,
            `https://${bucketName}.${correctRegion}`
          );
          console.log(
            `已添加 bucket 到 URL: ${bucketName}，使用区域: ${correctRegion}`
          );
        } else {
          // host 已经包含 bucket，检查区域是否正确
          if (parsedRegion !== correctRegion && configRegion) {
            // 如果区域不匹配且配置中有正确的区域，替换为正确的区域
            console.warn(
              `警告：host 中的区域 (${parsedRegion}) 与配置中的区域 (${correctRegion}) 不一致，使用配置中的正确区域`
            );
            uploadUrl = uploadUrl.replace(
              /https?:\/\/([^.]+)\.(oss-cn-[^.]+\.aliyuncs\.com)/,
              `https://${bucketName}.${correctRegion}`
            );
          } else if (ticket.bucket && parsedBucket !== ticket.bucket) {
            // 如果后端返回的 bucket 与 host 中的不一致，使用后端返回的 bucket
            console.warn(
              `警告：host 中的 bucket (${parsedBucket}) 与 ticket.bucket (${ticket.bucket}) 不一致，使用 ticket.bucket`
            );
            uploadUrl = uploadUrl.replace(
              /https?:\/\/([^.]+)\.(oss-cn-[^.]+\.aliyuncs\.com)/,
              `https://${ticket.bucket}.${correctRegion}`
            );
          }
        }
        // 移除URL末尾的斜杠（如果有）
        uploadUrl = uploadUrl.replace(/\/$/, "");

        // 5. 构建表单数据（OSS PostObject规范要求）
        // 注意：参数顺序很重要，按照OSS规范：key, policy, OSSAccessKeyId, signature
        const formData = {
          key: ossKey, // OSS中的文件路径（必须）
          policy: ticket.policy, // 策略文档（必须，Base64编码）
          OSSAccessKeyId: ticket.accessid, // Access Key ID（必须）
          signature: ticket.signature, // 签名（必须）
          success_action_status: "200", // 上传成功时返回200状态码（可选）
        };

        console.log("开始上传图片到OSS:", {
          originalFilePath: filePath,
          localFilePath: localFilePath,
          ossKey,
          uploadUrl,
          formDataKeys: Object.keys(formData),
          formData: {
            key: formData.key,
            policy: formData.policy
              ? `${formData.policy.substring(0, 20)}...`
              : "[空]",
            OSSAccessKeyId: formData.OSSAccessKeyId,
            signature: formData.signature
              ? `${formData.signature.substring(0, 10)}...`
              : "[空]",
            success_action_status: formData.success_action_status,
          },
        });

        // 6. 执行上传（使用uni.uploadFile）
        // uni.uploadFile 会自动将 formData 和 file 组合成 multipart/form-data 格式
        // 格式类似：------WebKitFormBoundary... Content-Disposition: form-data; name="key" ...
        const uploadResult = await new Promise((resolve, reject) => {
          uni.uploadFile({
            url: uploadUrl, // OSS PostObject endpoint URL
            filePath: localFilePath, // 本地文件路径（已处理网络路径）
            name: "file", // OSS PostObject规范要求文件字段名为'file'
            formData: formData, // 表单参数（key, policy, OSSAccessKeyId, signature等）
            // 注意：uni.uploadFile 会自动设置 Content-Type: multipart/form-data; boundary=...
            success: (res) => {
              console.log("OSS上传响应:", {
                statusCode: res.statusCode,
                data: res.data,
                header: res.header,
              });

              // OSS上传成功通常返回200或204状态码
              if (res.statusCode === 200 || res.statusCode === 204) {
                console.log("图片上传成功:", res);
                resolve(res);
              } else {
                const errorMsg = res.data
                  ? typeof res.data === "string"
                    ? res.data
                    : JSON.stringify(res.data)
                  : `上传失败，状态码: ${res.statusCode}`;
                console.error("上传失败，响应数据:", res);
                reject(new Error(errorMsg));
              }
            },
            fail: (err) => {
              console.error("上传请求失败:", err);
              reject(new Error(err.errMsg || "上传失败"));
            },
          });
        });

        // 7. 构建并返回图片的永久访问URL
        // 使用与 uploadUrl 相同的逻辑构建图片访问 URL
        let imageUrl = uploadUrl;
        // 确保 URL 格式正确：https://{bucket}.oss-cn-{region}.aliyuncs.com/{ossKey}
        imageUrl = `${imageUrl}/${ossKey}`;

        console.log("图片上传成功，永久URL:", imageUrl);
        return imageUrl;
      } catch (error) {
        console.error("上传图片到OSS失败:", error);
        throw new Error(error.message || "上传图片失败，请重试");
      }
    },
    /**
     * 格式化奖品数据，将接口返回的数据转换为页面展示格式
     * @param {Array} apiPrizes 接口返回的奖品数组
     * @returns {Array} 格式化后的奖品数组
     */
    formatPrizes(apiPrizes) {
      if (!Array.isArray(apiPrizes)) {
        return [];
      }

      return apiPrizes.map((prize) => {
        // 判断奖品类型
        const isVirtual =
          prize.prizeType === "VIRTUAL" ||
          prize.prizeType === "VIRTUAL_OR_PHYSICAL_UNSPECIFIED";
        const isCertificate =
          prize.name &&
          (prize.name.includes("证书") ||
            prize.name.toLowerCase().includes("certificate"));
        const type = isVirtual ? "virtual" : "physical";
        const typeText = isVirtual ? "虚拟奖品" : "实物奖品";

        // 判断状态和操作文本
        let status = "available";
        let actionText = "领取";

        // 如果有卡号和卡密，说明已发放，状态为 code（虚拟奖品）
        if (!isCertificate && prize.cardNumber) {
          status = "code";
          actionText = "查询兑换码";
        } else if (isCertificate) {
          status = "certificate";
          actionText = "点击查看";
        } else if (
          prize.recipientName ||
          prize.recipientPhone ||
          prize.recipientAddress
        ) {
          // 如果有收件人信息，说明已领取（实物奖品）
          status = "claimed";
          actionText = "已领取";
        } else if (!isVirtual) {
          // 实物奖品且没有收件人信息，需要门店领取
          status = "store";
          actionText = "门店领取";
        }
        // 虚拟奖品且没有卡号卡密，保持 available 状态

        // 格式化日期（drawDate 作为过期日期显示）
        let expireDate = "";
        if (prize.drawDate) {
          // 如果 drawDate 是完整日期，直接使用；否则可能需要格式化
          const date = new Date(prize.drawDate);
          if (!isNaN(date.getTime())) {
            // 计算过期日期（假设中奖后30天过期）
            date.setDate(date.getDate() + 30);
            const year = date.getFullYear();
            const month = String(date.getMonth() + 1).padStart(2, "0");
            const day = String(date.getDate()).padStart(2, "0");
            expireDate = `${year}-${month}-${day}`;
          } else {
            expireDate = prize.drawDate;
          }
        }

        return {
          id: prize.id || "",
          name: prize.name || "未知奖品",
          type: type,
          typeText: typeText,
          expireDate: expireDate,
          status: status,
          actionText: actionText,
          // 保留原始数据，用于后续操作
          originalData: prize,
        };
      });
    },
  },
};
</script>

<style>
.container {
  min-height: 100vh;
  background-color: #92d3f4;
  background-image: url("https://hykoc.oss-cn-beijing.aliyuncs.com/koc/tc2025/资源 19-8.png");
  background-size: 100% auto;
  background-position: top center;
  background-repeat: no-repeat;
  padding: 0;
}

/* 顶部区域 */
.header {
  padding: 20rpx 0 40rpx;
  position: relative;
}

.top-bar {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 30rpx;
}

.logo-section {
  display: flex;
  align-items: center;
  position: absolute;
  left: 0;
  top: 120rpx;
}

.fixed-logo {
  width: 180rpx;
  height: 40rpx;
}

.nav-icons {
  display: flex;
  flex-direction: column;
  gap: 0;
  position: absolute;
  right: 0;
  top: 180rpx;
  background: #4a4a4a;
  border-radius: 16rpx 0 0 16rpx;
  padding: 10rpx 6rpx;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
  z-index: 1000;
}

.nav-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12rpx;
  padding: 16rpx 12rpx;
  border-radius: 8rpx;
  transition: background-color 0.2s;
  position: relative;
}

.nav-button:active {
  background-color: rgba(255, 255, 255, 0.1);
}

/* 抽奖按钮动效 */
.lottery-button-active {
  animation: lotteryPulse 2s ease-in-out infinite;
}

@keyframes lotteryPulse {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.05);
  }
}

@keyframes badgeBounce {
  0%,
  100% {
    transform: translateY(0) scale(1);
  }
  50% {
    transform: translateY(-4rpx) scale(1.1);
  }
}

.nav-icon-img {
  width: 36rpx;
  height: 36rpx;
}

.nav-button-text {
  font-size: 24rpx;
  color: #ffffff;
  font-weight: 400;
}

.title-section {
  text-align: center;
  margin-bottom: 30rpx;
}

.main-title {
  display: block;
  font-size: 48rpx;
  font-weight: bold;
  color: #ffd700;
  text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.3);
  margin-bottom: 10rpx;
}

.season-text {
  display: block;
  font-size: 28rpx;
  color: #32cd32;
  font-weight: bold;
}

.path-illustration {
  position: relative;
  width: 100%;
  height: 400rpx;
  margin: 160rpx 0 30rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}

.path-bg {
  width: 100%;
  height: auto;
}

.path-elements {
  position: relative;
  height: 100%;
  z-index: 2;
}

.smartwatch {
  position: absolute;
  left: 20rpx;
  top: 20rpx;
  width: 60rpx;
  height: 60rpx;
}

.walking-people {
  position: absolute;
  left: 80rpx;
  top: 40rpx;
  width: 120rpx;
  height: 80rpx;
}

.character {
  position: absolute;
  left: 50%;
  top: 20rpx;
  transform: translateX(-50%);
  width: 80rpx;
  height: 80rpx;
}

.music-note {
  position: absolute;
  right: 200rpx;
  top: 30rpx;
  width: 40rpx;
  height: 40rpx;
}

.jiangsu-banner {
  position: absolute;
  right: 120rpx;
  top: 50rpx;
  width: 80rpx;
  height: 30rpx;
}

.sailboat {
  position: absolute;
  right: 20rpx;
  top: 10rpx;
  width: 60rpx;
  height: 60rpx;
}

/* 用户信息区域 */
.user-section {
  margin: 20rpx 20rpx 0 20rpx;
}

/* 蓝色卡片容器 */
.user-card {
  background: #4a90e2;
  border-radius: 20rpx 20rpx 0 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
  box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.15);
  padding: 5rpx 0;
}

/* 左侧统计 */
.stats-left {
  flex: 1;
  text-align: center;
  padding-right: 5rpx;
}

/* 右侧统计 */
.stats-right {
  flex: 1;
  text-align: center;
  padding-left: 5rpx;
}

/* 中间用户头像区域 */
.user-profile {
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  z-index: 10;
  transform: translateY(-60rpx);
}

/* 头像容器 */
.avatar-container {
  position: relative;
  margin-bottom: 10rpx;
}

/* 临时头像 */
.temp-avatar {
  width: 120rpx;
  height: 120rpx;
  border-radius: 50%;
  background: #ffa500;
  border: 6rpx solid #ff8c00;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.2);
}

.avatar-text {
  font-size: 24rpx;
  color: white;
  font-weight: bold;
}

.user-id {
  font-size: 24rpx;
  color: white;
  font-weight: bold;
  text-align: center;
  margin-top: 5rpx;
  max-width: 200rpx;
  word-wrap: break-word;
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* 头像可点击样式 */
.avatar-clickable {
  cursor: pointer;
  opacity: 0.9;
  transition: opacity 0.3s ease;
}

.avatar-clickable:active {
  opacity: 0.7;
  transform: scale(0.98);
}

/* 昵称可点击样式 */
.nickname-clickable {
  cursor: pointer;
  text-decoration: underline;
  text-decoration-style: dotted;
  opacity: 0.9;
  transition: opacity 0.3s ease;
}

.nickname-clickable:active {
  opacity: 0.7;
}

.stats-label {
  display: block;
  font-size: 24rpx;
  color: white;
  margin-bottom: 8rpx;
  text-align: center;
}

.stats-value {
  display: block;
  font-size: 44rpx;
  font-weight: bold;
  color: white;
  text-align: center;
}

.charity-fund {
  margin: 0rpx 4rpx;
  margin-bottom: 0;
}

.fund-label-container {
  width: 100%;
  overflow: hidden;
  margin-bottom: 20rpx;
  position: relative;
  text-align: center;
}

.fund-label {
  display: inline-block;
  font-size: 24rpx;
  color: #666;
  white-space: nowrap;
  width: max-content;
}

.fund-label-text-inner {
  display: inline-block;
  margin-right: 200rpx;
}

.fund-label-scroll {
  animation: fundLabelScroll 30s linear infinite;
}

@keyframes fundLabelScroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-50%);
  }
}

.fund-data-container {
  position: relative;
  width: 100%;
  height: 180rpx;
  margin-top: 20rpx;
}

.fund-background {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
}

/* 外层容器：负责匀速移动 */
.boy-outer {
  position: absolute;
  bottom: 25rpx;
  z-index: 3;
  /* 使用 transform 实现居中定位（基于 left 值） */
  transform: translateX(-50%);
  /* 默认不启用过渡 */
  transition: none;
}

/* 外层：开始匀速移动 */
.boy-outer.boy-moving {
  /* 匀速移动：使用 linear 实现从左向右匀速移动效果，持续2秒 */
  transition: left 2s linear !important;
}

/* 外层：到达目的地后，停止移动（移除过渡） */
/* 注意：只有在没有 boy-moving 类时才移除过渡 */
.boy-outer.boy-arrived:not(.boy-moving) {
  /* 到达目的地后停止移动，不再有过渡效果 */
  transition: none;
}

/* 内层图片：负责摇晃 */
.boy-inner {
  width: 60rpx;
  height: auto;
  display: block;
  /* 默认不摇晃 */
  animation: none;
  transform-origin: center bottom;
}

/* 内层：开始摇晃（移动过程中和到达后都摇晃） */
.boy-inner.boy-shaking {
  /* 旋转摇晃动画：保持恒定频率循环播放 */
  animation: boyRotate 1.5s ease-in-out infinite;
}

/* 旋转摇晃动画：只旋转，不移动位置 */
@keyframes boyRotate {
  0% {
    transform: rotate(-8deg);
  }
  25% {
    transform: rotate(8deg);
  }
  50% {
    transform: rotate(-8deg);
  }
  75% {
    transform: rotate(8deg);
  }
  100% {
    transform: rotate(-8deg);
  }
}

.current-fund {
  position: absolute;
  top: 10rpx;
  left: 30rpx;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.target-fund {
  position: absolute;
  top: 10rpx;
  right: 30rpx;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}

.fund-amount {
  font-size: 32rpx;
  font-weight: bold;
  color: #ff4444;
  margin-bottom: 5rpx;
}

.fund-label-text {
  font-size: 24rpx;
  color: #666;
}

/* 功能按钮区域 */
.action-buttons {
  /* margin由charity-fund提供 */
  background: #fff;
  border-radius: 50rpx 50rpx 0 0;
  padding: 10rpx 30rpx 0 30rpx;
  width: 100%;
  box-sizing: border-box;
  margin-top: -30rpx;
  position: relative;
  z-index: 10;
}

.primary-buttons {
  display: flex;
  justify-content: space-between;
  margin-bottom: 20rpx;
}

.image-button {
  width: 240rpx;
  height: 120rpx;
  margin: 0 5rpx;
}

.donation-buttons {
  display: flex;
  gap: 20rpx;
}

.donation-btn {
  flex: 1;
  height: 100rpx;
  background: #ff4444;
  color: white;
  border: none;
  border-radius: 25rpx;
  font-size: 36rpx;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1);
}

.donation-btn::after {
  border: none;
}

/* 个人公益捐步按钮动画效果 */
.donation-btn-personal {
  position: relative;
  animation: prominentPulse 1.2s ease-in-out infinite;
  overflow: visible;
}

/* 更明显的脉冲、缩放和光晕动画 */
@keyframes prominentPulse {
  0% {
    transform: scale(1);
    box-shadow: 0 8rpx 20rpx rgba(255, 68, 68, 0.5),
                0 0 0 0 rgba(255, 68, 68, 0.8);
  }
  50% {
    transform: scale(1.08);
    box-shadow: 0 16rpx 40rpx rgba(255, 68, 68, 0.8),
                0 0 0 18rpx rgba(255, 68, 68, 0);
  }
  100% {
    transform: scale(1);
    box-shadow: 0 8rpx 20rpx rgba(255, 68, 68, 0.5),
                0 0 0 0 rgba(255, 68, 68, 0.8);
  }
}

/* 活动时间信息 */
.event-info {
  background: linear-gradient(to top, #92d3f4, #ffffff);
  display: flex;
  align-items: center;
  padding: 20rpx 30rpx;
  gap: 80rpx;
}

/* 弹窗样式 */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
}

/* 外层容器 */
.modal-outer {
  width: 92%;
  max-width: 600rpx;
  background: linear-gradient(to bottom, #add8e6, #b8e6ff, #fff);
  border-radius: 20rpx;
  overflow: hidden;
  box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.3);
  padding: 6rpx;
}

/* 外层标题区域 */
.modal-header-outer {
  padding: 30rpx;
  text-align: center;
  background: transparent;
}

.modal-title-outer {
  font-size: 36rpx;
  font-weight: bold;
  color: #1e90ff;
}

/* 内层容器 */
.modal-inner {
  background-color: #ffffff;
  border: #add8e6 solid 1rpx;
  border-radius: 14rpx;
  display: flex;
  flex-direction: column;
  max-height: 70vh;
  width: calc(100% - 60rpx);
  margin: 0 auto;
  margin-bottom: 30rpx;
}

/* 滚动内容区域 */
.modal-content-scroll {
  flex: 1;
  max-height: 50vh;
}

.modal-body {
  padding: 40rpx 30rpx 20rpx;
}

.rule-item {
  margin-bottom: 35rpx;
  padding-bottom: 25rpx;
  border-bottom: 1rpx solid #f0f0f0;
}

.rule-item:last-child {
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: none;
}

.rule-title {
  display: block;
  font-size: 26rpx;
  font-weight: 600;
  color: #2c3e50;
  margin-bottom: 15rpx;
  line-height: 1.4;
}

.rule-detail {
  display: block;
  font-size: 24rpx;
  color: #5a6c7d;
  line-height: 1.6;
  padding-left: 20rpx;
  white-space: pre-line;
}

.rule-table {
  margin-top: 20rpx;
  border: 1rpx solid #e0e0e0;
  border-radius: 16rpx;
  overflow: hidden;
}

.rule-table-row {
  display: flex;
  border-bottom: 1rpx solid #e0e0e0;
}

.rule-table-row:last-child {
  border-bottom: none;
}

.rule-table-cell {
  flex: 1;
  padding: 16rpx 12rpx;
  font-size: 22rpx;
  color: #4a4a4a;
  line-height: 1.5;
  white-space: normal;
  word-break: break-word;
}

.rule-table-cell.code {
  flex: 0 0 130rpx;
  color: #2c3e50;
}

.rule-table-cell.address {
  flex: 1;
  text-align: center;
}

.rule-table-cell.color {
  flex: 0 0 90rpx;
  text-align: center;
  color: #2c3e50;
}

.rule-table-header {
  background: linear-gradient(135deg, #fef4f4, #ffe9e9);
}

.rule-table-header .rule-table-cell {
  font-weight: 600;
  color: #d93c3c;
}

.rule-table-row:nth-child(even):not(.rule-table-header) {
  background-color: #fff6f6;
}

.modal-footer {
  padding: 30rpx 40rpx 40rpx;
  text-align: center;
}

.modal-btn {
  width: 100%;
  height: 88rpx;
  background: linear-gradient(135deg, #ff6b6b, #ff5252);
  color: #ffffff;
  font-size: 30rpx;
  font-weight: 600;
  border: none;
  border-radius: 44rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4rpx 12rpx rgba(255, 107, 107, 0.3);
  transition: all 0.3s ease;
}

.modal-btn:active {
  transform: translateY(2rpx);
  box-shadow: 0 2rpx 8rpx rgba(255, 107, 107, 0.3);
}

.event-time-image {
  width: 100%;
  height: auto;
}

.offline-registration-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  padding: 20rpx 0;
  cursor: pointer;
  transition: opacity 0.3s ease;
}

.offline-registration-text {
  font-size: 32rpx;
  font-weight: bold;
  color: #ff4444;
  white-space: nowrap;
}

.offline-registration-btn:active {
  opacity: 0.7;
}

/* 奖品弹窗样式 */
.prize-modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(76, 76, 76, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
}

.prize-modal-container {
  width: 92%;
  max-width: 680rpx;
  height: 60vh;
  background-color: #fff9f5;
  border-radius: 40rpx 40rpx 20rpx 20rpx;
  overflow: hidden;
  box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
}

/* 头部固定区域 */
.prize-modal-header {
  background-color: #fff9f5;
  border-radius: 40rpx 40rpx 0 0;
  padding: 40rpx 30rpx 30rpx;
  position: relative;
  flex-shrink: 0;
}

.prize-modal-title {
  display: block;
  font-size: 36rpx;
  font-weight: bold;
  color: #ff4444;
  text-align: center;
}

.prize-modal-close {
  position: absolute;
  top: 20rpx;
  right: 10rpx;
  width: 60rpx;
  height: 60rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}

.close-icon-image {
  width: 60rpx;
  height: 60rpx;
}

/* 标签页导航 */
.prize-tabs {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0;
  padding: 0 30rpx;
  margin-bottom: 20rpx;
  background-color: #fff9f5;
  border-bottom: 1rpx solid #f0f0f0;
}

.prize-tab {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 24rpx 0;
  position: relative;
  cursor: pointer;
}

.prize-tab-text {
  font-size: 28rpx;
  color: #999999;
  transition: color 0.3s ease;
  position: relative;
  z-index: 1;
}

.prize-tab-active .prize-tab-text {
  color: #ff4444;
  font-weight: bold;
}

.prize-tab-active::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 110%;
  height: 4rpx;
  background-color: #ff4444;
  border-radius: 2rpx;
  z-index: 2;
}

/* 滚动内容区域 */
.prize-modal-content {
  flex: 1;
  overflow-y: auto;
  padding: 30rpx;
  box-sizing: border-box;
  width: 100%;
}

.prize-list {
  display: flex;
  flex-direction: column;
  gap: 20rpx;
  width: 100%;
  box-sizing: border-box;
}

/* 奖品卡片 */
.prize-card {
  box-sizing: border-box;
  background-color: #ffffff;
  border: 1rpx solid #e5e5e5;
  border-radius: 16rpx;
  padding: 30rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  max-width: 100%;
  overflow: hidden;
}

.prize-card-left {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 12rpx;
  margin-right: 20rpx;
  min-width: 0;
}

.prize-name {
  font-size: 32rpx;
  font-weight: bold;
  color: #333333;
  line-height: 1.4;
  word-break: break-all;
  overflow-wrap: break-word;
}

.prize-detail {
  font-size: 24rpx;
  color: #999999;
  line-height: 1.4;
  word-break: break-all;
  overflow-wrap: break-word;
}

.prize-card-right {
  flex-shrink: 0;
}

.prize-action-btn {
  padding: 16rpx 32rpx;
  background-color: #ff4444;
  color: #ffffff;
  border: none;
  border-radius: 30rpx;
  font-size: 26rpx;
  font-weight: 500;
  white-space: nowrap;
  line-height: 1;
}

.prize-action-btn-disabled {
  background-color: #cccccc;
  color: #ffffff;
}

.prize-action-btn::after {
  border: none;
}

/* 空状态提示样式 */
.prize-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 100rpx 30rpx;
  width: 100%;
  box-sizing: border-box;
}

.prize-empty-text {
  font-size: 32rpx;
  color: #999999;
  margin-bottom: 20rpx;
  font-weight: 500;
}

.prize-empty-hint {
  font-size: 26rpx;
  color: #cccccc;
  text-align: center;
  line-height: 1.5;
}

/* 奖品弹窗底部抽奖按钮区域 */
.prize-modal-footer {
  padding: 20rpx 30rpx 30rpx;
  border-top: 1rpx solid #f0f0f0;
  background-color: #fff9f5;
  flex-shrink: 0;
}

.lottery-button-in-modal {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 15rpx;
  width: 100%;
  height: 100rpx;
  background: linear-gradient(135deg, #ff6b6b, #ff5252);
  border-radius: 50rpx;
  box-shadow: 0 4rpx 12rpx rgba(255, 107, 107, 0.3);
  transition: all 0.3s ease;
  animation: none; /* 默认无动画，只有有抽奖次数时才应用动画 */
}

.lottery-button-in-modal:active {
  transform: translateY(2rpx);
  box-shadow: 0 2rpx 8rpx rgba(255, 107, 107, 0.3);
}

.lottery-button-in-modal.lottery-button-active {
  animation: lotteryPulse 2s ease-in-out infinite;
}

.lottery-icon-in-modal {
  width: 40rpx;
  height: 40rpx;
}

.lottery-text-in-modal {
  font-size: 32rpx;
  color: #ffffff;
  font-weight: bold;
}

.lottery-badge-in-modal {
  position: absolute;
  top: -8rpx;
  right: 20rpx;
  background: linear-gradient(135deg, #ff4444, #ff6b6b);
  color: #ffffff;
  font-size: 20rpx;
  font-weight: bold;
  min-width: 36rpx;
  height: 36rpx;
  line-height: 36rpx;
  text-align: center;
  border-radius: 18rpx;
  padding: 0 10rpx;
  box-shadow: 0 2rpx 8rpx rgba(255, 68, 68, 0.4);
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: badgeBounce 1.5s ease-in-out infinite;
}

/* 用户授权弹窗样式 */
.auth-modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 10000;
}

.auth-modal-container {
  width: 85%;
  max-width: 600rpx;
  background-color: #ffffff;
  border-radius: 30rpx;
  overflow: hidden;
  box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
}

.auth-modal-header {
  position: relative;
  padding: 40rpx 30rpx 20rpx;
  text-align: center;
  border-bottom: 1rpx solid #f0f0f0;
}

.auth-modal-title {
  font-size: 36rpx;
  font-weight: bold;
  color: #333333;
}

.auth-modal-close {
  position: absolute;
  top: 20rpx;
  right: 20rpx;
  width: 50rpx;
  height: 50rpx;
  background-color: #f5f5f5;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.auth-modal-close .close-icon {
  color: #999999;
  font-size: 36rpx;
  font-weight: bold;
  line-height: 1;
}

.auth-modal-body {
  padding: 40rpx 30rpx;
}

.auth-modal-desc {
  display: block;
  font-size: 28rpx;
  color: #666666;
  line-height: 1.6;
  margin-bottom: 30rpx;
  text-align: center;
}

.auth-info-list {
  display: flex;
  flex-direction: column;
  gap: 20rpx;
}

.auth-info-item {
  display: flex;
  align-items: center;
  gap: 15rpx;
  padding: 15rpx;
  background-color: #f8f8f8;
  border-radius: 12rpx;
}

.auth-info-icon {
  width: 32rpx;
  height: 32rpx;
  background-color: #4caf50;
  color: #ffffff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20rpx;
  font-weight: bold;
  flex-shrink: 0;
  line-height: 1;
  text-align: center;
}

.auth-info-text {
  font-size: 26rpx;
  color: #333333;
  line-height: 1.5;
  flex: 1;
}

.auth-modal-footer {
  display: flex;
  gap: 20rpx;
  padding: 20rpx 30rpx 40rpx;
  border-top: 1rpx solid #f0f0f0;
}

.auth-btn {
  flex: 1;
  height: 88rpx;
  border: none;
  border-radius: 44rpx;
  font-size: 30rpx;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}

.auth-btn::after {
  border: none;
}

.auth-btn-cancel {
  background-color: #f5f5f5;
  color: #666666;
}

.auth-btn-confirm {
  background: linear-gradient(135deg, #ff6b6b, #ff5252);
  color: #ffffff;
  box-shadow: 0 4rpx 12rpx rgba(255, 107, 107, 0.3);
}

.auth-btn:active {
  opacity: 0.8;
}

/* 新版本头像昵称填写表单样式 */
.new-user-info-form {
  display: flex;
  flex-direction: column;
  gap: 30rpx;
  margin-top: 20rpx;
}

.user-info-form-item {
  display: flex;
  flex-direction: column;
  gap: 15rpx;
}

.form-label {
  font-size: 28rpx;
  color: #333333;
  font-weight: 500;
}

.avatar-button {
  width: 160rpx;
  height: 160rpx;
  border-radius: 16rpx;
  padding: 0;
  margin: 0;
  border: 2rpx dashed #cccccc;
  background-color: #f8f8f8;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.avatar-button::after {
  border: none;
}

.avatar-preview {
  width: 100%;
  height: 100%;
  border-radius: 14rpx;
}

.avatar-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}

.avatar-placeholder text {
  font-size: 24rpx;
  color: #999999;
}

.nickname-input {
  width: 100%;
  height: 80rpx;
  padding: 0 20rpx;
  background-color: #f8f8f8;
  border: 2rpx solid #e0e0e0;
  border-radius: 12rpx;
  font-size: 28rpx;
  color: #333333;
  box-sizing: border-box;
}

.nickname-input:focus {
  border-color: #4caf50;
  background-color: #ffffff;
}

/* 手机号弹窗样式 */
.phone-modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 10001;
}

.phone-modal-container {
  width: 85%;
  max-width: 600rpx;
  background-color: #ffffff;
  border-radius: 30rpx;
  overflow: hidden;
  box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
}

.phone-modal-header {
  position: relative;
  padding: 40rpx 30rpx 20rpx;
  text-align: center;
  border-bottom: 1rpx solid #f0f0f0;
}

.phone-modal-title {
  font-size: 36rpx;
  font-weight: bold;
  color: #333333;
}

.phone-modal-close {
  position: absolute;
  top: 20rpx;
  right: 20rpx;
  width: 50rpx;
  height: 50rpx;
  background-color: #f5f5f5;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.phone-modal-close .close-icon {
  color: #999999;
  font-size: 36rpx;
  font-weight: bold;
  line-height: 1;
}

.phone-modal-body {
  padding: 40rpx 30rpx;
}

.phone-modal-desc {
  display: block;
  font-size: 28rpx;
  color: #666666;
  line-height: 1.6;
  margin-bottom: 30rpx;
  text-align: center;
}

.phone-form-item {
  display: flex;
  flex-direction: column;
  gap: 15rpx;
}

.phone-form-input {
  width: 100%;
  height: 80rpx;
  padding: 0 20rpx;
  padding-right: 140rpx;
  background-color: #f8f8f8;
  border: 2rpx solid #e0e0e0;
  border-radius: 12rpx;
  font-size: 28rpx;
  color: #333333;
  box-sizing: border-box;
}

.phone-form-input:focus {
  border-color: #4caf50;
  background-color: #ffffff;
}

.phone-modal-footer {
  display: flex;
  gap: 20rpx;
  padding: 20rpx 30rpx 40rpx;
  border-top: 1rpx solid #f0f0f0;
}

.phone-btn {
  flex: 1;
  height: 88rpx;
  border: none;
  border-radius: 44rpx;
  font-size: 30rpx;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}

.phone-btn::after {
  border: none;
}

.phone-btn-cancel {
  background-color: #f5f5f5;
  color: #666666;
}

.phone-btn-confirm {
  background: linear-gradient(135deg, #ff6b6b, #ff5252);
  color: #ffffff;
  box-shadow: 0 4rpx 12rpx rgba(255, 107, 107, 0.3);
}

.phone-btn:active {
  opacity: 0.8;
}

.phone-input-wrapper {
  position: relative;
  width: 100%;
}

.phone-input {
  padding-right: 140rpx;
}

.wechat-auth-btn {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: auto;
  height: auto;
  line-height: 1;
  padding: 0 20rpx;
  margin: 0;
  background: transparent;
  border: none;
  font-size: 24rpx;
  color: #4caf50;
  z-index: 1;
}

.wechat-auth-btn::after {
  border: none;
}

.wechat-auth-text {
  position: absolute;
  right: 20rpx;
  top: 50%;
  transform: translateY(-50%);
  font-size: 24rpx;
  color: #4caf50;
  z-index: 1;
}

/* 用户头像样式 */
.user-avatar {
  width: 120rpx;
  height: 120rpx;
  border-radius: 50%;
  border: 6rpx solid #ff8c00;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.2);
}

/* 团队弹窗样式 */
.team-modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3000;
  animation: fadeIn 0.3s ease-in-out;
}

.team-modal-container {
  width: 85%;
  max-width: 600rpx;
  background-color: #ffffff;
  border-radius: 30rpx;
  overflow: hidden;
  animation: scaleIn 0.3s ease-in-out;
}

.team-modal-header {
  padding: 40rpx 30rpx 20rpx;
  text-align: center;
  border-bottom: 1rpx solid #f0f0f0;
}

.team-modal-title {
  font-size: 36rpx;
  font-weight: bold;
  color: #333333;
}

.team-modal-body {
  padding: 40rpx 30rpx;
  text-align: center;
}

.team-modal-text {
  font-size: 28rpx;
  color: #666666;
  line-height: 1.6;
  display: block;
}

.team-modal-footer {
  display: flex;
  gap: 20rpx;
  padding: 20rpx 30rpx 40rpx;
  border-top: 1rpx solid #f0f0f0;
}

.team-modal-btn {
  flex: 1;
  height: 88rpx;
  background: #ff4444;
  color: white;
  border: none;
  border-radius: 44rpx;
  font-size: 32rpx;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4rpx 12rpx rgba(255, 68, 68, 0.3);
}

.team-modal-btn:active {
  transform: scale(0.98);
  box-shadow: 0 2rpx 8rpx rgba(255, 68, 68, 0.2);
}

.team-modal-btn-cancel {
  background: #f5f5f5;
  color: #666666;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}

.team-modal-btn-cancel:active {
  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}

.team-modal-btn-confirm {
  background: #ff4444;
  color: white;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes scaleIn {
  from {
    transform: scale(0.9);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}

/* 成功弹窗遮罩层（用于需要先去捐步弹窗） */
.success-modal-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  animation: fadeIn 0.3s ease-in-out;
}

/* 成功弹窗内容 */
.success-modal-content {
  position: relative;
  width: 600rpx;
  max-width: 90%;
  display: flex;
  flex-direction: column;
  align-items: center;
  animation: scaleIn 0.3s ease-in-out;
}

/* 关闭按钮 */
.close-button {
  position: absolute;
  top: 20rpx;
  right: -20rpx;
  width: 60rpx;
  height: 60rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1001;
  cursor: pointer;
}

/* 关闭图标 */
.close-icon {
  width: 60rpx;
  height: 60rpx;
}

/* 背景图片容器 */
.success-modal-bg-wrapper {
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-bottom: 150rpx;
}

/* 背景图片 */
.success-modal-bg {
  width: 600rpx;
  height: 800rpx;
  border-radius: 20rpx;
}

/* 吉祥物图片 */
.success-modal-mascot {
  position: absolute;
  top: 32%;
  left: 50%;
  transform: translateX(-50%);
  width: 200rpx;
  height: 200rpx;
  z-index: 10;
}

/* 提示文字 */
.success-text {
  position: absolute;
  top: 65%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: #ff0000;
  font-size: 66rpx;
  font-weight: bold;
  text-align: center;
  z-index: 10;
  text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.success-text.small {
  font-size: 42rpx;
}

.success-text text {
  display: block;
  line-height: 1.5;
}

/* 去捐步按钮 */
.lottery-button {
  position: absolute;
  bottom: 40rpx;
  left: 50%;
  transform: translateX(-50%);
  width: 400rpx;
  height: 88rpx;
  background: #f03c32;
  border-radius: 44rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  font-size: 36rpx;
  font-weight: bold;
  box-shadow: 0 8rpx 24rpx rgba(255, 0, 0, 0.4);
  transition: all 0.3s ease;
  z-index: 10;
}

.lottery-button:active {
  transform: translateX(-50%) scale(0.95);
  box-shadow: 0 4rpx 12rpx rgba(255, 0, 0, 0.3);
}

/* 确认按钮容器 */
.confirm-buttons-container {
  position: absolute;
  bottom: 40rpx;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 100rpx;
  justify-content: center;
  align-items: center;
}

.confirm-button-cancel,
.confirm-button-confirm {
  position: relative;
  transform: none;
  width: 180rpx;
  height: 88rpx;
  background: #f03c32;
  border-radius: 44rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  font-size: 32rpx;
  font-weight: bold;
  box-shadow: 0 4rpx 12rpx rgba(240, 60, 50, 0.3);
}

.confirm-button-cancel:active,
.confirm-button-confirm:active {
  transform: scale(0.95);
  box-shadow: 0 4rpx 12rpx rgba(255, 0, 0, 0.3);
}

/* 证书弹窗样式 */
.certificate-modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 12000;
}

.certificate-modal-container {
  width: 92%;
  max-width: 680rpx;
  max-height: 90vh;
  background-color: #ffffff;
  border-radius: 24rpx;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  position: relative;
}

.certificate-close {
  position: absolute;
  top: 16rpx;
  right: 16rpx;
  width: 60rpx;
  height: 60rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}

.certificate-close-icon {
  width: 60rpx;
  height: 60rpx;
}

.certificate-content {
  flex: 1;
  padding: 0;
}

.certificate-image {
  width: 100%;
  border-radius: 0;
  display: block;
}

.certificate-footer {
  padding: 20rpx;
  border-top: 1rpx solid #f0f0f0;
}

.certificate-save-btn {
  width: 100%;
  height: 88rpx;
  background: #ff4444;
  color: #ffffff;
  border: none;
  border-radius: 44rpx;
  font-size: 32rpx;
  font-weight: bold;
}

.certificate-save-btn::after {
  border: none;
}
</style>
